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

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

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

Структуры, которые нам понадобятся:
C++:
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++:
void Function(Arg* arg)
{
    arg->pLoadLibraryA(arg->user32);
    arg->pMessageBoxA(0, arg->msg, arg->title, 0);
}
Найдем процесс, откроем его, найдем адрес и размер интересующего нас модуля (откуда мы будем выполнять наш поток), создадим структуру аргумента и заполним ее
C++:
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++:
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++:
    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++:
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++:
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


Исходный код всего проекта -
Пожалуйста, авторизуйтесь для просмотра ссылки.
 
off
Участник
Статус
Оффлайн
Регистрация
7 Сен 2017
Сообщения
696
Реакции[?]
163
Поинты[?]
0
Привет, опять я хуйней маюсь, да
Сегодня у нас гайд как запустить свою функцию в другом процессе без дополнительного выделения памяти под нее

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

Структуры, которые нам понадобятся:
C++:
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++:
void Function(Arg* arg)
{
    arg->pLoadLibraryA(arg->user32);
    arg->pMessageBoxA(0, arg->msg, arg->title, 0);
}
Найдем процесс, откроем его, найдем адрес и размер интересующего нас модуля (откуда мы будем выполнять наш поток), создадим структуру аргумента и заполним ее
C++:
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++:
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++:
    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++:
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++:
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


Исходный код всего проекта -
Пожалуйста, авторизуйтесь для просмотра ссылки.
иди крякай скит
 
got a brand new bitch
Забаненный
Статус
Оффлайн
Регистрация
28 Дек 2018
Сообщения
544
Реакции[?]
59
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Модератор форума
Модератор
Статус
Оффлайн
Регистрация
19 Май 2018
Сообщения
954
Реакции[?]
1,067
Поинты[?]
20K
got a brand new bitch
Забаненный
Статус
Оффлайн
Регистрация
28 Дек 2018
Сообщения
544
Реакции[?]
59
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
cpuid inc.
Забаненный
Статус
Оффлайн
Регистрация
6 Авг 2019
Сообщения
1,071
Реакции[?]
760
Поинты[?]
1K
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
не выделять память отдельно, а искать пустое пространство
Для чего настолько костылить? Какая вероятность что пустое пространство не используется никем и там нет важных данных? Какая вероятность того что размер памяти будет такой как тебе нужно? Абсолютно бессмысленно с точки зрения мануалмапа.
 
Модератор форума
Модератор
Статус
Оффлайн
Регистрация
19 Май 2018
Сообщения
954
Реакции[?]
1,067
Поинты[?]
20K
Абсолютно бессмысленно с точки зрения мануалмапа.
запустишь поток из памяти, которая не принадлежит никакому модулю, - получишь по шапке от античита
Какая вероятность того что размер памяти будет такой как тебе нужно?
в смысле
Какая вероятность что пустое пространство не используется никем и там нет важных данных?
какая секция в модуле будет всегда полностью заполнена? в конце всегда будет место, причем пустое, явно хватит, чтобы вставить туда свою функцию, выполнить ее, а после вернуть все как было
 
cpuid inc.
Забаненный
Статус
Оффлайн
Регистрация
6 Авг 2019
Сообщения
1,071
Реакции[?]
760
Поинты[?]
1K
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
запустишь поток из памяти, которая не принадлежит никакому модулю, - получишь по шапке от античита
Что за мемы 21 века. Какой анти-чит следит за всеми модулями? Это как минимум глупо.
Ну твоя функа ищет пустое пространство n-го размера. Каков шанс что я найду размер который мне требуется? Уже проёб.
какая секция в модуле будет всегда полностью заполнена? в конце всегда будет место, причем пустое, явно хватит, чтобы вставить туда свою функцию, выполнить ее, а после вернуть все как было
Оно там и не просто так лежит. Куда умнее было бы кидать в исполняемые регионы созданные JIT'ом.
 
Модератор форума
Модератор
Статус
Оффлайн
Регистрация
19 Май 2018
Сообщения
954
Реакции[?]
1,067
Поинты[?]
20K
Что за мемы 21 века. Какой анти-чит следит за всеми модулями? Это как минимум глупо.
ну ладно выдели память и запусти поток оттуда в любой игре с еаком
Ну твоя функа ищет пустое пространство n-го размера. Каков шанс что я найду размер который мне требуется? Уже проёб.
ты для virtualalloc тоже размер ищешь?) или твоя функция настолько огромная, что ей 4096 байтов мало?
 
cpuid inc.
Забаненный
Статус
Оффлайн
Регистрация
6 Авг 2019
Сообщения
1,071
Реакции[?]
760
Поинты[?]
1K
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
ну ладно выдели память и запусти поток оттуда в любой игре с еаком
Т.е таким костылём ты обходишь еак? Интересно же он работает, если трассирует каждый поток.
ты для virtualalloc тоже размер ищешь?) или твоя функция настолько огромная, что ей 4096 байтов мало?
Т.е анти-чит спокойно реагирует на создание памяти с потока 'x' при этом у этой памяти есть флаги 'r/w/x', верно?
 
Эксперт
Статус
Оффлайн
Регистрация
12 Июн 2014
Сообщения
991
Реакции[?]
1,209
Поинты[?]
3K
Что за мемы 21 века. Какой анти-чит следит за всеми модулями? Это как минимум глупо.
ты будешь крайне удивлен...

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

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

Абсолютно бессмысленно с точки зрения мануалмапа
на самом деле идея(как таковая) вполне нормальная, реализация и узконаправленность в хакинг конечно сомнительна, но вполне применима
 
cpuid inc.
Забаненный
Статус
Оффлайн
Регистрация
6 Авг 2019
Сообщения
1,071
Реакции[?]
760
Поинты[?]
1K
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
если память пуста и не используется на момент обращения, то тебе не чего не мешает ее зарезервировать для себя
>На момент обращения.
И долго будешь ловить такой момент? Любая память для чего-то используется.
ищи нужный размер или дроби кусками
Дробим функцию на функции.
на самом деле идея(как таковая) вполне нормальная, реализация и узконаправленность в хакинг конечно сомнительна, но вполне применима
Нет. Куда логичнее искать те же исполняемые регионы созданные JIT'ом и туда пихать свой код.
 
Эксперт
Статус
Оффлайн
Регистрация
12 Июн 2014
Сообщения
991
Реакции[?]
1,209
Поинты[?]
3K
cpuid inc.
Забаненный
Статус
Оффлайн
Регистрация
6 Авг 2019
Сообщения
1,071
Реакции[?]
760
Поинты[?]
1K
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
где ты это прочитал или кто над тобой так пошутил?
Ну а хули нам разработчикам, VirtualAlloc есть а вот VirtualFree украли.
И патчим вызовы на другие адреса? Если мне нужно разбить функцию то как я это сделаю то?
 
Сверху Снизу