Гайд Запускаем удаленный поток из любого модуля

Пользователь форума
Эксперт
Эксперт
Статус
Оффлайн
Регистрация
19 Май 2018
Сообщения
959
Реакции
1,067
Привет, опять я хуйней маюсь, да
Сегодня у нас гайд как запустить свою функцию в другом процессе без дополнительного выделения памяти под нее

Мини-алгоритм:
  1. Находим пустое пространство в модуле под функцию
  2. Находим пустое пространство под аргумент
  3. Меняем защиту на этих адресных пространствах
  4. Записываем в память функцию и аргумент
  5. Выполняем
  6. Обнуляем
  7. Ставим протект обратно
В качестве процесса, где будет выполняться наш код я буду использовать простую консоль, которая выводит This is infinite loop каждую секунду

Структуры, которые нам понадобятся:
C++:
Expand Collapse Copy
struct ModInfo
{
    DWORD Base;
    DWORD Size;
};

struct Arg
{
    fnLoadLibraryA pLoadLibraryA; // адрес LoadLibraryA для загрузки модулей, откуда мы возьмем импорт MessageBoxA
    fnMessageBoxA pMessageBoxA; // сам MessageBoxA
    char msg[255];
    char title[255];
    char user32[255];
};

Сама фунция, которая будет вызвана:
C++:
Expand Collapse Copy
void Function(Arg* arg)
{
    arg->pLoadLibraryA(arg->user32);
    arg->pMessageBoxA(0, arg->msg, arg->title, 0);
}

Найдем процесс, откроем его, найдем адрес и размер интересующего нас модуля (откуда мы будем выполнять наш поток), создадим структуру аргумента и заполним ее
C++:
Expand Collapse Copy
DWORD ProcessId = FindProcessId("empty.exe");
if (!ProcessId)
{
    cout << "Counldn't find process\n";
    Quit(1);
}

HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
if (hProc == INVALID_HANDLE_VALUE || !hProc)
{
    cout << "Failed to open process\n";
    Quit(1);
}

LPCSTR Module = "ntdll.dll"; // модуль, из которого мы будем запускать поток (выбрать можете любой, но учтите, что он должен существовать в процессе)
ModInfo Info = GetModInfo(Module, ProcessId);

Arg arg;
arg.pLoadLibraryA = LoadLibraryA;
arg.pMessageBoxA = MessageBoxA;
strcpy(arg.msg, "message");
strcpy(arg.title, "title");
strcpy(arg.user32, "user32.dll");

Функция, с помощью которой мы найдем пустое пространство:
C++:
Expand Collapse Copy
DWORD FreeSpaceInModule(const HANDLE& hProc, const ModInfo& Info, const DWORD& Size)
{
// скопируем модуль себе
    BYTE* Bytes = new BYTE[Info.Size];
    ReadProcessMemory(hProc, (LPCVOID)(Info.Base), Bytes, Info.Size, nullptr);

    int Matches = 0;
// найдем адрес, начиная с которого в модуле будет 00 Size раз
    for (int i = 0; i < Info.Size - 1; i++)
    {
        if (Bytes[i] == 0)
            Matches++;
        else
            Matches = 0;

        if (Matches == Size)
        {
            delete[] Bytes;
            return Info.Base + i;
        }
    }

    delete[] Bytes;
    return 0;
}

Этой функцией найдем пустые пространства под функцию и аргумент, сменим протект, запишем функцию, аргумент
C++:
Expand Collapse Copy
    PVOID pFunc = (PVOID)FreeSpaceInModule(hProc, Info, FunctionLength);
    PVOID pArg = (PVOID)FreeSpaceInModule(hProc, Info, sizeof(arg));
    DWORD OldProtect_Function = 0;
    DWORD OldProtect_Arg = 0;

    if (!pFunc || !pArg)
    {
        cout << "Couldn't find free memory in " << Module << endl;
        CloseHandle(hProc);
        Quit(1);
    }

    cout << "Found free memory for function at 0x" << pFunc << endl;
    cout << "Found free memory for argument at 0x" << pArg << endl;

    if (!VirtualProtectEx(hProc, pFunc, FunctionLength, PAGE_EXECUTE_READWRITE, &OldProtect_Function))
    {
        cout << "VirtualProtectEx (1) failed with status: " << GetLastError() << endl;
        CloseHandle(hProc);
        Quit(1);
    }
    
    if (!VirtualProtectEx(hProc, pArg, sizeof(arg), PAGE_EXECUTE_READWRITE, &OldProtect_Arg))
    {
        cout << "VirtualProtectEx (2) failed with status: " << GetLastError() << endl;
        VirtualProtectEx(hProc, pFunc, FunctionLength, OldProtect_Function, &OldProtect_Function);
        CloseHandle(hProc);
        Quit(1);
    }
      
    if (!WriteProcessMemory(hProc, pFunc, Function, FunctionLength, nullptr))
    {
        cout << "WriteProcessMemory (1) failed with status: " << GetLastError() << endl;
        VirtualProtectEx(hProc, pFunc, FunctionLength, OldProtect_Function, &OldProtect_Function);
        VirtualProtectEx(hProc, pFunc, sizeof(arg), OldProtect_Arg, &OldProtect_Arg);
        CloseHandle(hProc);
        Quit(1);
    }

    if (!WriteProcessMemory(hProc, pArg, &arg, sizeof(arg), nullptr))
    {
        cout << "WriteProcessMemory (2) failed with status: " << GetLastError() << endl;
        VirtualProtectEx(hProc, pFunc, FunctionLength, OldProtect_Function, &OldProtect_Function);
        VirtualProtectEx(hProc, pFunc, sizeof(arg), OldProtect_Arg, &OldProtect_Arg);
        CloseHandle(hProc);
        Quit(1);
    }

Запустим сам поток, дождемся его завершения и закроем дескриптор
C++:
Expand Collapse Copy
HANDLE hThread = CreateRemoteThread(hProc, nullptr, 0, (LPTHREAD_START_ROUTINE)pFunc, pArg, 0, nullptr);
if (hThread == INVALID_HANDLE_VALUE || !hThread)
{
    cout << "Failed to create remote thread\n";
    Quit(1);
}

WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);

Так как размер моей функции я нашел, я могу обнулить ее саму в своем процессе и записать уже обнуленные байты в удаленный процесс
Размер своей функции ищите сами, а лучше ставьте размер 0х1000 и для обнуления создавайте массив из 0х1000 байт, обнуляйте его и пишите уже относительно его
Я использую фиксированный размер, т.к. моя функция маленькая и по пизде вряд-ли что-то пойдет

Так вот, снимем протект у себя в процессе со своей функции, обнулим, поставим протект обратно, обнулим аргумент, запишем аргумент и функцию в удаленный процесс так, как мы это делали до этого, после чего установим исходный протект и закроем процесс
C++:
Expand Collapse Copy
DWORD OldFunctionProtect1 = 0;
VirtualProtect(Function, FunctionLength, PAGE_EXECUTE_READWRITE, &OldFunctionProtect1);
ZeroMemory(&arg, sizeof(arg));
ZeroMemory(Function, FunctionLength);
VirtualProtect(Function, FunctionLength, OldFunctionProtect1, &OldFunctionProtect1);
WriteProcessMemory(hProc, pFunc, Function, FunctionLength, nullptr);
WriteProcessMemory(hProc, pArg, &arg, sizeof(arg), nullptr);
VirtualProtectEx(hProc, pFunc, FunctionLength, OldProtect_Function, &OldProtect_Function);
VirtualProtectEx(hProc, pFunc, sizeof(arg), OldProtect_Arg, &OldProtect_Arg);

CloseHandle(hProc);

Запустим empty.exe и наш процесс:
1585586039769.png



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

Мини-алгоритм:
  1. Находим пустое пространство в модуле под функцию
  2. Находим пустое пространство под аргумент
  3. Меняем защиту на этих адресных пространствах
  4. Записываем в память функцию и аргумент
  5. Выполняем
  6. Обнуляем
  7. Ставим протект обратно
В качестве процесса, где будет выполняться наш код я буду использовать простую консоль, которая выводит This is infinite loop каждую секунду

Структуры, которые нам понадобятся:
C++:
Expand Collapse Copy
struct ModInfo
{
    DWORD Base;
    DWORD Size;
};

struct Arg
{
    fnLoadLibraryA pLoadLibraryA; // адрес LoadLibraryA для загрузки модулей, откуда мы возьмем импорт MessageBoxA
    fnMessageBoxA pMessageBoxA; // сам MessageBoxA
    char msg[255];
    char title[255];
    char user32[255];
};

Сама фунция, которая будет вызвана:
C++:
Expand Collapse Copy
void Function(Arg* arg)
{
    arg->pLoadLibraryA(arg->user32);
    arg->pMessageBoxA(0, arg->msg, arg->title, 0);
}

Найдем процесс, откроем его, найдем адрес и размер интересующего нас модуля (откуда мы будем выполнять наш поток), создадим структуру аргумента и заполним ее
C++:
Expand Collapse Copy
DWORD ProcessId = FindProcessId("empty.exe");
if (!ProcessId)
{
    cout << "Counldn't find process\n";
    Quit(1);
}

HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
if (hProc == INVALID_HANDLE_VALUE || !hProc)
{
    cout << "Failed to open process\n";
    Quit(1);
}

LPCSTR Module = "ntdll.dll"; // модуль, из которого мы будем запускать поток (выбрать можете любой, но учтите, что он должен существовать в процессе)
ModInfo Info = GetModInfo(Module, ProcessId);

Arg arg;
arg.pLoadLibraryA = LoadLibraryA;
arg.pMessageBoxA = MessageBoxA;
strcpy(arg.msg, "message");
strcpy(arg.title, "title");
strcpy(arg.user32, "user32.dll");

Функция, с помощью которой мы найдем пустое пространство:
C++:
Expand Collapse Copy
DWORD FreeSpaceInModule(const HANDLE& hProc, const ModInfo& Info, const DWORD& Size)
{
// скопируем модуль себе
    BYTE* Bytes = new BYTE[Info.Size];
    ReadProcessMemory(hProc, (LPCVOID)(Info.Base), Bytes, Info.Size, nullptr);

    int Matches = 0;
// найдем адрес, начиная с которого в модуле будет 00 Size раз
    for (int i = 0; i < Info.Size - 1; i++)
    {
        if (Bytes[i] == 0)
            Matches++;
        else
            Matches = 0;

        if (Matches == Size)
        {
            delete[] Bytes;
            return Info.Base + i;
        }
    }

    delete[] Bytes;
    return 0;
}

Этой функцией найдем пустые пространства под функцию и аргумент, сменим протект, запишем функцию, аргумент
C++:
Expand Collapse Copy
    PVOID pFunc = (PVOID)FreeSpaceInModule(hProc, Info, FunctionLength);
    PVOID pArg = (PVOID)FreeSpaceInModule(hProc, Info, sizeof(arg));
    DWORD OldProtect_Function = 0;
    DWORD OldProtect_Arg = 0;

    if (!pFunc || !pArg)
    {
        cout << "Couldn't find free memory in " << Module << endl;
        CloseHandle(hProc);
        Quit(1);
    }

    cout << "Found free memory for function at 0x" << pFunc << endl;
    cout << "Found free memory for argument at 0x" << pArg << endl;

    if (!VirtualProtectEx(hProc, pFunc, FunctionLength, PAGE_EXECUTE_READWRITE, &OldProtect_Function))
    {
        cout << "VirtualProtectEx (1) failed with status: " << GetLastError() << endl;
        CloseHandle(hProc);
        Quit(1);
    }
   
    if (!VirtualProtectEx(hProc, pArg, sizeof(arg), PAGE_EXECUTE_READWRITE, &OldProtect_Arg))
    {
        cout << "VirtualProtectEx (2) failed with status: " << GetLastError() << endl;
        VirtualProtectEx(hProc, pFunc, FunctionLength, OldProtect_Function, &OldProtect_Function);
        CloseHandle(hProc);
        Quit(1);
    }
     
    if (!WriteProcessMemory(hProc, pFunc, Function, FunctionLength, nullptr))
    {
        cout << "WriteProcessMemory (1) failed with status: " << GetLastError() << endl;
        VirtualProtectEx(hProc, pFunc, FunctionLength, OldProtect_Function, &OldProtect_Function);
        VirtualProtectEx(hProc, pFunc, sizeof(arg), OldProtect_Arg, &OldProtect_Arg);
        CloseHandle(hProc);
        Quit(1);
    }

    if (!WriteProcessMemory(hProc, pArg, &arg, sizeof(arg), nullptr))
    {
        cout << "WriteProcessMemory (2) failed with status: " << GetLastError() << endl;
        VirtualProtectEx(hProc, pFunc, FunctionLength, OldProtect_Function, &OldProtect_Function);
        VirtualProtectEx(hProc, pFunc, sizeof(arg), OldProtect_Arg, &OldProtect_Arg);
        CloseHandle(hProc);
        Quit(1);
    }

Запустим сам поток, дождемся его завершения и закроем дескриптор
C++:
Expand Collapse Copy
HANDLE hThread = CreateRemoteThread(hProc, nullptr, 0, (LPTHREAD_START_ROUTINE)pFunc, pArg, 0, nullptr);
if (hThread == INVALID_HANDLE_VALUE || !hThread)
{
    cout << "Failed to create remote thread\n";
    Quit(1);
}

WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);

Так как размер моей функции я нашел, я могу обнулить ее саму в своем процессе и записать уже обнуленные байты в удаленный процесс
Размер своей функции ищите сами, а лучше ставьте размер 0х1000 и для обнуления создавайте массив из 0х1000 байт, обнуляйте его и пишите уже относительно его
Я использую фиксированный размер, т.к. моя функция маленькая и по пизде вряд-ли что-то пойдет

Так вот, снимем протект у себя в процессе со своей функции, обнулим, поставим протект обратно, обнулим аргумент, запишем аргумент и функцию в удаленный процесс так, как мы это делали до этого, после чего установим исходный протект и закроем процесс
C++:
Expand Collapse Copy
DWORD OldFunctionProtect1 = 0;
VirtualProtect(Function, FunctionLength, PAGE_EXECUTE_READWRITE, &OldFunctionProtect1);
ZeroMemory(&arg, sizeof(arg));
ZeroMemory(Function, FunctionLength);
VirtualProtect(Function, FunctionLength, OldFunctionProtect1, &OldFunctionProtect1);
WriteProcessMemory(hProc, pFunc, Function, FunctionLength, nullptr);
WriteProcessMemory(hProc, pArg, &arg, sizeof(arg), nullptr);
VirtualProtectEx(hProc, pFunc, FunctionLength, OldProtect_Function, &OldProtect_Function);
VirtualProtectEx(hProc, pFunc, sizeof(arg), OldProtect_Arg, &OldProtect_Arg);

CloseHandle(hProc);

Запустим empty.exe и наш процесс:
Посмотреть вложение 65704


Исходный код всего проекта -
Пожалуйста, авторизуйтесь для просмотра ссылки.
иди крякай скит
 
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
не выделять память отдельно, а искать пустое пространство
Для чего настолько костылить? Какая вероятность что пустое пространство не используется никем и там нет важных данных? Какая вероятность того что размер памяти будет такой как тебе нужно? Абсолютно бессмысленно с точки зрения мануалмапа.
 
Абсолютно бессмысленно с точки зрения мануалмапа.
запустишь поток из памяти, которая не принадлежит никакому модулю, - получишь по шапке от античита
Какая вероятность того что размер памяти будет такой как тебе нужно?
в смысле
Какая вероятность что пустое пространство не используется никем и там нет важных данных?
какая секция в модуле будет всегда полностью заполнена? в конце всегда будет место, причем пустое, явно хватит, чтобы вставить туда свою функцию, выполнить ее, а после вернуть все как было
 
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
запустишь поток из памяти, которая не принадлежит никакому модулю, - получишь по шапке от античита
Что за мемы 21 века. Какой анти-чит следит за всеми модулями? Это как минимум глупо.
Ну твоя функа ищет пустое пространство n-го размера. Каков шанс что я найду размер который мне требуется? Уже проёб.
какая секция в модуле будет всегда полностью заполнена? в конце всегда будет место, причем пустое, явно хватит, чтобы вставить туда свою функцию, выполнить ее, а после вернуть все как было
Оно там и не просто так лежит. Куда умнее было бы кидать в исполняемые регионы созданные JIT'ом.
 
Что за мемы 21 века. Какой анти-чит следит за всеми модулями? Это как минимум глупо.
ну ладно выдели память и запусти поток оттуда в любой игре с еаком
Ну твоя функа ищет пустое пространство n-го размера. Каков шанс что я найду размер который мне требуется? Уже проёб.
ты для virtualalloc тоже размер ищешь?) или твоя функция настолько огромная, что ей 4096 байтов мало?
 
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
ну ладно выдели память и запусти поток оттуда в любой игре с еаком
Т.е таким костылём ты обходишь еак? Интересно же он работает, если трассирует каждый поток.
ты для virtualalloc тоже размер ищешь?) или твоя функция настолько огромная, что ей 4096 байтов мало?
Т.е анти-чит спокойно реагирует на создание памяти с потока 'x' при этом у этой памяти есть флаги 'r/w/x', верно?
 
Что за мемы 21 века. Какой анти-чит следит за всеми модулями? Это как минимум глупо.
ты будешь крайне удивлен...

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

Какая вероятность того что размер памяти будет такой как тебе нужно?
ищи нужный размер или дроби кусками

Абсолютно бессмысленно с точки зрения мануалмапа
на самом деле идея(как таковая) вполне нормальная, реализация и узконаправленность в хакинг конечно сомнительна, но вполне применима
 
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
если память пуста и не используется на момент обращения, то тебе не чего не мешает ее зарезервировать для себя
>На момент обращения.
И долго будешь ловить такой момент? Любая память для чего-то используется.
ищи нужный размер или дроби кусками
Дробим функцию на функции.
на самом деле идея(как таковая) вполне нормальная, реализация и узконаправленность в хакинг конечно сомнительна, но вполне применима
Нет. Куда логичнее искать те же исполняемые регионы созданные JIT'ом и туда пихать свой код.
 
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
где ты это прочитал или кто над тобой так пошутил?
Ну а хули нам разработчикам, VirtualAlloc есть а вот VirtualFree украли.
И патчим вызовы на другие адреса? Если мне нужно разбить функцию то как я это сделаю то?
 
Назад
Сверху Снизу