Войти или зарегистрироваться
  • Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.
  • Данный раздел создан исключительно для релизов! Вопросы по лаунчеру или обвязке задавайте ТОЛЬКО в соответсвующей теме автора. Любые другие темы будут удалены, а авторы понесут наказание.

Лаунчер [C++17|Avn2]: Пишем защиту вместе! #7 Виртуализация на службе у читера

#4: Защищаемся от инжектов через APC

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

Для начала разберёмся, что такое APC.
APC - Asynchronous Procedure Call - механизм асинхронного вызова процедур, реализованный в ядре Windows. Основное его предназначение - межпроцессное и межпоточное взаимодействие (RPC), организация очереди задач для пула потоков, а также, исходя из названия, поддержка асинхронных вызовов функций.

У каждого потока есть своя собственная очередь APC, которую тот опустошает, исполняя накопившиеся APC, когда переходит в специальное Alertable-состояние. APC не могут быть доставлены потоку, пока тот не находится в Alertable-состоянии, и накапливаются в очереди.

Чтобы перейти в Alertable-состояние, поток должен уйти в ожидание с помощью функций SleepEx, WaitForSingleObjectEx, WaitForMultipleObjectsEx или MsgWaitForMultipleObjectsEx, передав TRUE в параметр bAlertable. В этом случае поток уходит в Alertable-ожидание и исполняет накопленные APC, пока ожидаемый объект (в случае [Msg]WaitFor***-функций) не перейдёт в сигнальное состояние, после чего поток выходит из Alertable-состояния и продолжает исполнение в обычном режиме.

Ядро может форсировать доставку юзермодных APC потокам, которые НЕ находятся в Alertable-состоянии. Пример такого кода в драйвере: ЗДЕСЬ.

Также, поток может сам очистить свою очередь APC, выполнив их специальной функцией NtTestAlert.

Чтобы добавить APC в очередь конкретного потока, существует функция QueueUserAPC в библиотеке kernel32.dll, которая сводится к сервису NtQueueApcThread в ntdll.dll.

Пример инжекта через APC похож на способ с CreateRemoteThread(LoadLibrary), только, вместо создания нового потока, мы используем уже существующий, заставляя его подгружать нашу библиотеку:
C++:
#include <Windows.h>

int main()
{
    CHAR LibPath[] = "C:\\Folder\\Library.dll"; // Путь к дллке, которую хотим подгрузить
    DWORD ProcessId = 1234; // ID целевого процесса
    DWORD ThreadId = 5678; // ID целевого потока в целевом процессе

    // Открываем целевой процесс:
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);

    // Открываем целевой поток:
    HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId);

    // Получаем адрес функции LoadLibrary, которую выполним в чужом процессе:
    PVOID pLoadLibrary = GetProcAddress(GetModuleHandle(L"kernel32"), "LoadLibraryA");

    // Выделяем в целевом процессе память под путь к дллке:
    PVOID pRemoteLibPath = VirtualAllocEx(
        hProcess,
        NULL,
        sizeof(LibPath),
        MEM_RESERVE | MEM_COMMIT,
        PAGE_READWRITE
    );

    // Записываем путь в память процесса:
    DWORD Written = 0;
    WriteProcessMemory(hProcess, pRemoteLibPath, LibPath, sizeof(LibPath), &Written);

    // Добавляем APC в очередь потока:
    QueueUserAPC((PAPCFUNC)pLoadLibrary, hThread, (ULONG_PTR)pRemoteLibPath);

    CloseHandle(hThread);
    CloseHandle(hProcess);
}
Этим кодом мы добавили в очередь потока APC с точкой входа на функции LoadLibrary, в качестве аргумента которой задали путь к библиотеке, которую хотим подгрузить. Осталось дождаться, пока поток перейдёт в Alertable-состояние и выполнит нашу LoadLibrary. Однако, стоит помнить, что поток может вообще не переходить в Alertable-состояние (тогда наш APC не выполнится никогда), поэтому нужно специально искать потоки, которые вызывают SleepEx или [Msg]WaitFor***Ex-функции.

Чтобы понять, как защищаться от этого типа инжектов, рассмотрим, как происходит доставка APC потоку. Напишем простой код доставки APC самому себе и посмотрим, кто и откуда вызывает нашу процедуру.
C++:
#include <Windows.h>
#include <winternl.h>

#pragma comment(lib, "ntdll.lib")

#define NtCurrentThread() ((HANDLE)-2)

extern "C" NTSYSAPI NTSTATUS NTAPI NtQueueApcThread(
    IN HANDLE ThreadHandle,
    IN PIO_APC_ROUTINE ApcRoutine,
    IN OPTIONAL PVOID ApcRoutineContext,
    IN OPTIONAL PIO_STATUS_BLOCK ApcStatusBlock,
    IN ULONG ApcReserved
);

extern "C" NTSYSAPI NTSTATUS NTAPI NtTestAlert();

static VOID NTAPI ApcRoutine(
    IN PVOID ApcContext,
    IN PIO_STATUS_BLOCK IoStatusBlock,
    IN ULONG Reserved
) {
    __debugbreak(); // Остановимся здесь
}

int main()
{
    // Ставим APC в очередь сами себе:
    NtQueueApcThread(
        NtCurrentThread(),
        ApcRoutine,
        NULL,
        NULL,
        0
    );

    // Очищаем очередь APC:
    NtTestAlert();

    return 0;
}
Посмотрим трейс потока:
upload_2019-3-26_22-14-29.png​

Нас интересует процедура KiUserApcDispatcher. Это юзермодный каллбэк, который вызывает ядро для доставки APC нашему потоку.

upload_2019-3-26_22-31-46.png

Рассмотрим подробнее, что происходило с потоком:
1. Добавили APC с точкой входа на нашей функции ApcRoutine в очередь APC нашего потока с помощью функции NtQueueApcThread
1. Ушли в ядро на сервисе NtTestAlert, переведя поток в Alertable-состояние
2. Ядро стало доставлять накопившиеся APC нашему потоку (в данном случае, в очереди стоит только APC с точкой входа на нашей функции ApcRoutine)
3. Для доставки APC наш поток из ядра вызывает юзермодный каллбэк KiUserApcDispatcher, в котором происходит вызов заданной нами функции
4. APC отрабатывает и поток возвращается на то место, где прервал работу, с помощью сервиса NtContinue - таким образом, выходим из NtTestAlert.

Для фильтрации APC перехватим KiUserApcDispatcher, чтобы анализировать точки входа и отсекать недоверенные. Здесь нас ждёт небольшая трудность: так как KiUserApcDispatcher - не обычная функция, а каллбэк из ядра, все аргументы передаются в стеке, что противоречит принятому в х64 соглашению fastcall. На чистом C/C++ мы не сможем реализовать корректный обработчик, т.к. компилятор не знает, как ему обрабатывать переданные аргументы, поэтому напишем прослойку на ассемблере, оборачивающую аргументы в понятный сишному коду вид с последующим вызовом обработчика на C/C++. Прототипы KiUserApcDispatcher на х32 и х64 отличаются:
C++:
// Для х64:
__declspec(noreturn)
VOID NTAPI KiUserApcDispatcher(CONTEXT Context);

// Для x32:
__declspec(noreturn)
VOID NTAPI KiUserApcDispatcher(
    PVOID NormalRoutine,
    PVOID SystemArgument1,
    PVOID SystemArgument2,
    CONTEXT Context
);
На x64 адрес APC хранится в поле Context.P4Home; аргумент - в поле Context.P1Home.
На х32 под адрес APC и аргумент выделены специальные аргументы - NormalRoutine и SystemArgument соответственно.
Всё остальное содержимое структуры Context - контекст, который следует восстановить, чтобы вернуть поток на место, где его прервали для обработки APC.
Таким образом, если хотим отменить вызов APC и сразу вернуться на продолжение оригинального кода, достаточно вызвать NtContinue(&Context).

Итак, напишем ассемблерную прослойку, приводящую аргументы на стеке в понятный сишному коду вид и вызовем сишный обработчик, в котором безусловно отменим все APC:
upload_2019-3-26_22-58-45.png
​
И сишный обработчик:
C++:
static BOOL IsApcAllowed(PVOID ApcRoutine)
{
    // Здесь должны быть проверки, но для примера запретим все APC:
    return FALSE;
}

[[noreturn]]
static inline VOID DiscardApc(PCONTEXT Context)
{
    NtContinue(Context, FALSE);
}

extern "C" {
    VOID NTAPI KiApcStub(); // Импортируем прослойку из ассемблерного кода

#ifdef _AMD64_
    // Адрес оригинального диспетчера, полученный после хука:
    VOID (NTAPI *OriginalApcDispatcher)(CONTEXT Context) = NULL;
     
    VOID NTAPI ApcHandler(PCONTEXT Context)
    {
        // ApcRoutine = Context->P4Home, Arg = Context->P1Home:
        if (!IsApcAllowed(reinterpret_cast<PVOID>(Context->P4Home)))
            DiscardApc(Context);
    }
#else
    // Адрес оригинального диспетчера, полученный после хука:
    VOID(NTAPI* OriginalApcDispatcher)(
        PVOID NormalRoutine,   // ApcProc
        PVOID SystemArgument1, // Argument
        PVOID SystemArgument2,
        CONTEXT Context
    ) = NULL;

    VOID NTAPI ApcHandler(PVOID ApcRoutine, PVOID Arg, PCONTEXT Context)
    {
        if (!IsApcAllowed(ApcRoutine))
            DiscardApc(Context);
    }
#endif
Всё, что остаётся - перехватить KiUserApcDispatcher любой библиотекой для хуков, в качестве хука задать ассемблерный KiApcStub и записать адрес оригинального диспетчера в переменную OriginalApcDispatcher.

KiApcStub вызовет наш ApcHandler, в котором мы можем провести полноценную фильтрацию и, при необходимости, отменить вызов APC, вернув контекст потока обратно функцией NtContinue(&Context).
 
#5: Защита от угона контекста

От угона контекста защититься нельзя.

image


Для начала, рассмотрим, что это такое.
Контекст - это состояние потока в определённый момент времени: значения всех регистров, содержимое стека, а также, адрес следующей инструкции, которую должен выполнить процессор на следующем шаге.

Контекст описывается структурой CONTEXT и для работы с контекстом существуют две функции: GetThreadContext и SetThreadContext. Используя их, мы можем в любой момент времени перекинуть поток на произвольный код с заданным нами состоянием регистров и содержимым стека. Это позволяет нам выполнять любые функции с любыми сигнатурами без каких-либо ограничений.

Так как контекст подменяется внутри ядра, нет ни одного способа это обнаружить или предотвратить.

Напишем класс, позволяющий выполнять шеллы в 64х-битных процессах:
C++:
#include <Windows.h>

class Shell {
#pragma pack(push, 1)
    typedef struct _SHELL_MAPPING {
        const unsigned char Reserved0[145];
        const void* Arg;
        const unsigned char Reserved1[2];
        const void* Func;
        const unsigned char Reserved2[147];
    } SHELL_MAPPING, *PSHELL_MAPPING;
#pragma pack(pop)
    unsigned char Code[310] = {
        /* +000 */ 0x9C, // pushfq
        /* +001 */ 0x50, // push rax
        /* +002 */ 0x51, // push rcx
        /* +003 */ 0x52, // push rdx
        /* +004 */ 0x53, // push rbx
        /* +005 */ 0x54, // push rsp
        /* +006 */ 0x55, // push rbp
        /* +007 */ 0x56, // push rsi
        /* +008 */ 0x57, // push rdi
        /* +009 */ 0x41, 0x50, // push r8
        /* +011 */ 0x41, 0x51, // push r9
        /* +013 */ 0x41, 0x52, // push r10
        /* +015 */ 0x41, 0x53, // push r11
        /* +017 */ 0x41, 0x54, // push r12
        /* +019 */ 0x41, 0x55, // push r13
        /* +021 */ 0x41, 0x56, // push r14
        /* +023 */ 0x41, 0x57, // push r15

        /* +025 */ 0x48, 0x81, 0xEC, 0x00, 0x01, 0x00, 0x00, // sub rsp, 16 * sizeof(OWORD)
        /* +032 */ 0x0F, 0x29, 0x04, 0x24,                               // movaps [rsp + 0  * sizeof(OWORD)], xmm0
        /* +036 */ 0x0F, 0x29, 0x4C, 0x24, 0x10,                         // movaps [rsp + 1  * sizeof(OWORD)], xmm1
        /* +041 */ 0x0F, 0x29, 0x54, 0x24, 0x20,                         // movaps [rsp + 2  * sizeof(OWORD)], xmm2
        /* +046 */ 0x0F, 0x29, 0x5C, 0x24, 0x30,                         // movaps [rsp + 3  * sizeof(OWORD)], xmm3
        /* +051 */ 0x0F, 0x29, 0x64, 0x24, 0x40,                         // movaps [rsp + 4  * sizeof(OWORD)], xmm4
        /* +056 */ 0x0F, 0x29, 0x6C, 0x24, 0x50,                         // movaps [rsp + 5  * sizeof(OWORD)], xmm5
        /* +061 */ 0x0F, 0x29, 0x74, 0x24, 0x60,                         // movaps [rsp + 6  * sizeof(OWORD)], xmm6
        /* +066 */ 0x0F, 0x29, 0x7C, 0x24, 0x70,                         // movaps [rsp + 7  * sizeof(OWORD)], xmm7
        /* +071 */ 0x44, 0x0F, 0x29, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, // movaps [rsp + 8  * sizeof(OWORD)], xmm8
        /* +080 */ 0x44, 0x0F, 0x29, 0x8C, 0x24, 0x90, 0x00, 0x00, 0x00, // movaps [rsp + 9  * sizeof(OWORD)], xmm9
        /* +089 */ 0x44, 0x0F, 0x29, 0x94, 0x24, 0xA0, 0x00, 0x00, 0x00, // movaps [rsp + 10 * sizeof(OWORD)], xmm10
        /* +098 */ 0x44, 0x0F, 0x29, 0x9C, 0x24, 0xB0, 0x00, 0x00, 0x00, // movaps [rsp + 11 * sizeof(OWORD)], xmm11
        /* +107 */ 0x44, 0x0F, 0x29, 0xA4, 0x24, 0xC0, 0x00, 0x00, 0x00, // movaps [rsp + 12 * sizeof(OWORD)], xmm12
        /* +116 */ 0x44, 0x0F, 0x29, 0xAC, 0x24, 0xD0, 0x00, 0x00, 0x00, // movaps [rsp + 13 * sizeof(OWORD)], xmm13
        /* +125 */ 0x44, 0x0F, 0x29, 0xB4, 0x24, 0xE0, 0x00, 0x00, 0x00, // movaps [rsp + 14 * sizeof(OWORD)], xmm14
        /* +134 */ 0x44, 0x0F, 0x29, 0xBC, 0x24, 0xF0, 0x00, 0x00, 0x00, // movaps [rsp + 15 * sizeof(OWORD)], xmm15

        /* +143 */ 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rcx, Arg
        /* +153 */ 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, Func
        /* +163 */ 0xFF, 0xD0, // call rax

        /* +165 */ 0x44, 0x0F, 0x29, 0xBC, 0x24, 0xF0, 0x00, 0x00, 0x00, // movaps xmm15, [rsp + 15 * sizeof(OWORD)]
        /* +174 */ 0x44, 0x0F, 0x29, 0xB4, 0x24, 0xE0, 0x00, 0x00, 0x00, // movaps xmm14, [rsp + 14 * sizeof(OWORD)]
        /* +183 */ 0x44, 0x0F, 0x29, 0xAC, 0x24, 0xD0, 0x00, 0x00, 0x00, // movaps xmm13, [rsp + 13 * sizeof(OWORD)]
        /* +192 */ 0x44, 0x0F, 0x29, 0xA4, 0x24, 0xC0, 0x00, 0x00, 0x00, // movaps xmm12, [rsp + 12 * sizeof(OWORD)]
        /* +201 */ 0x44, 0x0F, 0x29, 0x9C, 0x24, 0xB0, 0x00, 0x00, 0x00, // movaps xmm11, [rsp + 11 * sizeof(OWORD)]
        /* +210 */ 0x44, 0x0F, 0x29, 0x94, 0x24, 0xA0, 0x00, 0x00, 0x00, // movaps xmm10, [rsp + 10 * sizeof(OWORD)]
        /* +219 */ 0x44, 0x0F, 0x29, 0x8C, 0x24, 0x90, 0x00, 0x00, 0x00, // movaps xmm9,  [rsp + 9 * sizeof(OWORD)]
        /* +228 */ 0x44, 0x0F, 0x29, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, // movaps xmm8,  [rsp + 8 * sizeof(OWORD)]
        /* +237 */ 0x0F, 0x29, 0x7C, 0x24, 0x70,                         // movaps xmm7,  [rsp + 7 * sizeof(OWORD)]
        /* +242 */ 0x0F, 0x29, 0x74, 0x24, 0x60,                         // movaps xmm6,  [rsp + 6 * sizeof(OWORD)]
        /* +247 */ 0x0F, 0x29, 0x6C, 0x24, 0x50,                         // movaps xmm5,  [rsp + 5 * sizeof(OWORD)]
        /* +252 */ 0x0F, 0x29, 0x64, 0x24, 0x40,                         // movaps xmm4,  [rsp + 4 * sizeof(OWORD)]
        /* +257 */ 0x0F, 0x29, 0x5C, 0x24, 0x30,                         // movaps xmm3,  [rsp + 3 * sizeof(OWORD)]
        /* +262 */ 0x0F, 0x29, 0x54, 0x24, 0x20,                         // movaps xmm2,  [rsp + 2 * sizeof(OWORD)]
        /* +267 */ 0x0F, 0x29, 0x4C, 0x24, 0x10,                         // movaps xmm1,  [rsp + 1 * sizeof(OWORD)]
        /* +272 */ 0x0F, 0x29, 0x04, 0x24,                               // movaps xmm0,  [rsp + 0 * sizeof(OWORD)]
        /* +276 */ 0x48, 0x81, 0xC4, 0x00, 0x01, 0x00, 0x00, // add rsp, 16 * sizeof(OWORD)

        /* +283 */ 0x41, 0x5F, // pop r15
        /* +285 */ 0x41, 0x5E, // pop r14
        /* +287 */ 0x41, 0x5D, // pop r13
        /* +289 */ 0x41, 0x5C, // pop r12
        /* +291 */ 0x41, 0x5B, // pop r11
        /* +293 */ 0x41, 0x5A, // pop r10
        /* +295 */ 0x41, 0x59, // pop r9
        /* +297 */ 0x41, 0x58, // pop r8
        /* +299 */ 0x5F, // pop rdi
        /* +300 */ 0x5E, // pop rsi
        /* +301 */ 0x5D, // pop rbp
        /* +302 */ 0x5C, // pop rsp
        /* +303 */ 0x5B, // pop rbx
        /* +304 */ 0x5A, // pop rdx
        /* +305 */ 0x59, // pop rcx
        /* +306 */ 0x58, // pop rax
        /* +307 */ 0x9D, // popfq
        /* +308 */ 0xC3, // ret
        /* +309 */ 0xCC, // int 3h (breakpoint)
    };
    static_assert(sizeof(SHELL_MAPPING) == sizeof(Code), "sizeof(SHELL_MAPPING) != sizeof(Code)");

public:
    Shell(LPCVOID FuncAddr, LPCVOID Arg)
    {
        auto Mapping = reinterpret_cast<PSHELL_MAPPING>(Code);
        Mapping->Func = FuncAddr;
        Mapping->Arg = Arg;
    }

    BOOL Execute(DWORD ProcessId, DWORD ThreadId)
    {
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
        HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId);
        if (!hProcess || !hThread) {
            if (hProcess) CloseHandle(hProcess);
            if (hThread) CloseHandle(hThread);
            return FALSE;
        }
          
        PVOID CodeBuf = VirtualAllocEx(hProcess, NULL, sizeof(Code), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        WriteProcessMemory(hProcess, CodeBuf, Code, sizeof(Code), NULL);
   
        SuspendThread(hThread);

        CONTEXT Context = {};
        Context.ContextFlags = CONTEXT_ALL;
        GetThreadContext(hThread, &Context);

        Context.Rsp -= sizeof(PVOID);
        WriteProcessMemory(hProcess, reinterpret_cast<PVOID>(Context.Rsp), &Context.Rip, sizeof(Context.Rip), NULL);

        Context.Rip = reinterpret_cast<DWORD64>(CodeBuf);
        SetThreadContext(hThread, &Context);
   
        ResumeThread(hThread);

        CloseHandle(hThread);
        CloseHandle(hProcess);

        return TRUE;
    }
};

В коде выше мы, несмотря на кажущуюся громоздкость, делаем простые вещи:
1. Генерируем шелл, который:
- Сохраняет состояние регистров
- Вызывает заданную функцию с прототипом "VOID WINAPI Func(PVOID Arg)" с заданным аргументом
- Восстанавливает состояние регистров
- Возвращается по адресу, который лежит на вершине стека (в п.2 мы его туда положим)
2. Вызываем шелл из заданного потока, подменяя его контекст:
- Выделяем в процессе память и записываем туда наш шелл
- Замораживаем целевой поток
- Получаем его контекст
- На вершину стека кладём адрес следующей инструкции, чтобы шелл после выполнения вернулся на то место, откуда мы угнали поток - таким образом, выполнение потока продолжится, как ни в чём не бывало
- Подменяем адрес следующей инструкции на адрес нашего шелла
- Размораживаем поток
- Поток начинает исполнять шелл, сохраняет на стеке все регистры, вызывает заданную нами функцию, затем восстанавливает регистры и возвращается обратно на ту точку, откуда мы угнали поток, продолжая его нормальное исполнение

Что мы можем этому противопоставить?
Для начала, следует научиться отличать свою память от чужой: поставить фильтры на выделение исполняемой памяти - перехватить NtAllocateVirtualMemory, NtFreeVirtualMemory и NtProtectVirtualMemory, чтобы точно знать, какую память выделил наш процесс (эта память пройдёт через наши фильтры), а какую память выделили со стороны (этой памяти в списке известных нам регионов не будет, т.к. она не пройдёт через фильтры).
Используя этот фильтр, мы можем периодически (например, по таймеру) проверять всю память процесса и искать неизвестные исполняемые регионы.
Другой вариант - перехватить функции, которые теоретически могут быть вызваны из шелла (например, LoadLibrary или NtContinue, которая может быть использована для возврата из шелла на оригинальный код потока), чтобы проверять стактрейс потока: если увидим фрейм, который лежит не в модуле и не в известной нам памяти - считаем это за инжект.

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

Однако, существует ещё один способ из разряда "клиент хочет странного": написать визор - бинарный транслятор, который будет исполнять машинный код в отдельном буфере или в анклаве. Это даст возможность сделать всю память процесса неисполняемой (за исключением системных точек входа внутри ntdll - Ki*** и некоторые Ldr***/Rtl***-функции) и шелл на первой же API-функции вылетит с ошибкой доступа. Этот способ сложный и выходит далеко за рамки этой статьи.
 
Последнее редактирование:
Данную защиту уже можно ставить на играбельный проект ? Есть ли какие-нибдьь эксперементабельные фичи, который стоит пока что не включать ?
 
Данную защиту уже можно ставить на играбельный проект ? Есть ли какие-нибдьь эксперементабельные фичи, который стоит пока что не включать ?
Пока нигде не тестировалось, дождись вайтлистов и поддержки правил блокировок, чтобы пропускать инжекты от валидных библиотек
 
Пока нигде не тестировалось, дождись вайтлистов и поддержки правил блокировок, чтобы пропускать инжекты от валидных библиотек
Ага ага, когда можно будет маскировать читы под валидные либы:D. А то ни разу ж не интересно.
 
Ага ага, когда можно будет маскировать читы под валидные либы:D. А то ни разу ж не интересно.
Читы читами, но главное не защититься от всего на свете, а ничего при этом не сломать. А что до читов - замаскировать под валидную либу уже сложнее, чем просто беспрепятственно инжектить любым паблик-инжектором, это уже отсечёт какой-то процент читоделов.
 
Читы читами, но главное не защититься от всего на свете, а ничего при этом не сломать. А что до читов - замаскировать под валидную либу уже сложнее, чем просто беспрепятственно инжектить любым паблик-инжектором, это уже отсечёт какой-то процент читоделов.
Ага, читы станут платными, как под ваймворлд.
 
Ага, читы станут платными, как под ваймворлд.
А это даже хорошо, обе стороны в плюсе: читоделам прибыль и отваливаются читеры с пабликами, а модераторы добавляют новые приватки в блэклист (мы же знаем, кто в нас инжектит, нам нужно лишь решить, доверенный это софт или нет), и всё по кругу.
 
Пока нигде не тестировалось, дождись вайтлистов и поддержки правил блокировок, чтобы пропускать инжекты от валидных библиотек
Когда примерно стоит ожидать ? =)
 
Exit code -1073741511
Гугл машет прощайнескучай
Экстрасенсов тут нет - при каких условиях, какое приложение, какой софт установлен, есть ли антивирусы или фаерволл, встроенная или дискретная видеокарта и какого производителя, стоят ли фирменные драйвера, какая версия операционки, какие параметры включены, что в AvnLog?

Сам по себе Exit code вообще ни о чём не говорит.
 
Последнее редактирование:
Есть какие-нибудь новости на счет обновлений ? :rolleyes:
Сделал систему репортов и базовую проверку исполняемых секций, но ещё нет биндингов к джаве.
Сейчас в доработке:
- Проверка только нужных модулей, чтобы не тратить ресурсы на ненужные дллки
- Проверка памяти на предмет неизвестных участков
- Собственно, биндинги к джаве и API

Может, на выходных\праздниках что-то появится
 
Сделал систему репортов и базовую проверку исполняемых секций, но ещё нет биндингов к джаве.
Сейчас в доработке:
- Проверка только нужных модулей, чтобы не тратить ресурсы на ненужные дллки
- Проверка памяти на предмет неизвестных участков
- Собственно, биндинги к джаве и API

Может, на выходных\праздниках что-то появится
=)
 
Экстрасенсов тут нет - при каких условиях, какое приложение, какой софт установлен, есть ли антивирусы или фаерволл, встроенная или дискретная видеокарта и какого производителя, стоят ли фирменные драйвера, какая версия операционки, какие параметры включены, что в AvnLog?

Сам по себе Exit code вообще ни о чём не говорит.
Так понятнее?
Windows 7 x32
wrapper32.png
 
Дичь какая.
В документации никаких ограничений на версию Windows для этой функции не накладывается.
https://docs.microsoft.com/en-us/windows/desktop/api/winnt/nf-winnt-rtlpctofileheader
Или проблема в твоей kernel32.dll, или в доках, где действительно забыли указать, что эта функция доступна с какой-то более новой версии Windows (однако, исходя из её предназначения, она должна быть доступна, как минимум с Win 2000).

Если скинешь свои kernel32.dll, kernelbase.dll и ntdll.dll, которые лежат в C:\Windows\System32, будет совсем здорово и поможет понять, действительно ли этой функции там нет и, если так, что используется вместо неё.
 
Дичь какая.
В документации никаких ограничений на версию Windows для этой функции не накладывается.
https://docs.microsoft.com/en-us/windows/desktop/api/winnt/nf-winnt-rtlpctofileheader
Или проблема в твоей kernel32.dll, или в доках, где действительно забыли указать, что эта функция доступна с какой-то более новой версии Windows (однако, исходя из её предназначения, она должна быть доступна, как минимум с Win 2000).

Если скинешь свои kernel32.dll, kernelbase.dll и ntdll.dll, которые лежат в C:\Windows\System32, будет совсем здорово и поможет понять, действительно ли этой функции там нет и, если так, что используется вместо неё.
Кину вечером в лс.
Может, данные о пк поспособствуют чему-то?
4124683277d0fea3e55ff46e7fef9ec1.png
 
Кину вечером в лс.
Может, данные о пк поспособствуют чему-то?
4124683277d0fea3e55ff46e7fef9ec1.png
> By AlexSoft
Возможно, из-за неоригинальной сборки...
Импорты всё равно поправлю (их можно вытащить не из kernel32, а из ntdll), но всё равно интересно, почему у тебя в k32 их нет, хотя по докам быть должны (например, у меня на Win10 x64 они есть)

upload_2019-5-29_13-5-45.png
 
> By AlexSoft
Возможно, из-за неоригинальной сборки...
Импорты всё равно поправлю (их можно вытащить не из kernel32, а из ntdll), но всё равно интересно, почему у тебя в k32 их нет, хотя по докам быть должны (например, у меня на Win10 x64 они есть)

Посмотреть вложение 53953
Кинул в лс.
Тащемта первый запуск был вполне нормальный. А вот когда второй раз запустил враппер, все полетело к черту.
После этого авангвард отклеивается на постоянной основе.
 

Поделиться этой страницей

Назад
Верх