Информация Гайд Вызываем удаленно игровую функцию и WinAPI

CleanLegend

Известный
Автор темы
Всефорумный модератор
474
927
Привет, сегодня мы будем вызывать игровую функцию из exe.
Пример вызова в dll:
C++:
#define FUNC_CMessages__AddMessageJumpQ 0x69F1E0
void AddMessageJumpQ(char *text, unsigned int time, unsigned short flag, bool bPreviousBrief)
{
    ((void(__cdecl *)(char *, unsigned int, unsigned short, bool))FUNC_CMessages__AddMessageJumpQ)(text, time, flag, bPreviousBrief);
}

AddMessageJumpQ("hello", 500, NULL,false); // вызываем

Для вызова из exe мы будем использовать:
-OpenProcess
-VirtualAllocEx
-WriteProcessMemory
-CreateRemoteThread

Создаем структуру для передачи аргументов в удаленный поток(CreateRemoteThread):
C++:
struct Struct
{
    char text[256];
    unsigned int time;
    unsigned short flag;
    bool bPreviousBrief;
    DWORD pAdr;
}FuncArgs;
Создаем прототип:
C++:
typedef void(__cdecl *_SendMSG)(char*, unsigned int, unsigned short, bool);
Создаем нашу функцию, которую будем вызывать. Под ней создаем еще одну, для получения размера
C++:
DWORD __stdcall RemoteThread(Struct *sArg)
{
    _SendMSG msg = (_SendMSG)sArg->pAdr; // передаем адрес
    msg(sArg->text, sArg->time, sArg->flag, sArg->bPreviousBrief); // вызываем
    return 0;
}
void __stdcall RemoteThread_end() {}
Заполняем структуру:
C++:
strcpy(FuncArgs.text, "Hello world!");
FuncArgs.time = 500;
FuncArgs.flag = NULL;
FuncArgs.bPreviousBrief = false;
FuncArgs.pAdr = 0x69F1E0;
Функция для получения id процесса:
C++:
DWORD GetProcId(const char* procname)
{
    PROCESSENTRY32 pe;
    HANDLE hSnap;

    pe.dwSize = sizeof(PROCESSENTRY32);
    hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    if (Process32First(hSnap, &pe)) {
        do {
            if (strcmp(pe.szExeFile, procname) == 0)
                break;
        } while (Process32Next(hSnap, &pe));
    }
    return pe.th32ProcessID;
}

C++:
// получаем хэндл процесса
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcId("gta_sa.exe"));
    // Выделяем память под наш поток в gta_sa
    LPVOID pRemoteThread = VirtualAllocEx(hProcess, NULL, (DWORD_PTR)RemoteThread_end - (DWORD_PTR)RemoteThread, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    // Записываем его
    WriteProcessMemory(hProcess, pRemoteThread, (LPVOID)RemoteThread, ((DWORD_PTR)RemoteThread_end - (DWORD_PTR)RemoteThread), 0);
    // Выделяем память для нашего объекта в gta_sa
    Struct *myArg = (Struct*)VirtualAllocEx(hProcess, NULL, sizeof(Struct), MEM_COMMIT, PAGE_READWRITE);
    // Записываем его
    WriteProcessMemory(hProcess, myArg, &FuncArgs, sizeof(Struct), NULL);
    // Запускаем наш поток pRemoteThread с аргументами myArg
    HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)pRemoteThread, myArg, 0, 0);
    // Закрываем дескриптор потока
    CloseHandle(hThread);
    // Освобождаем выделенную память
    VirtualFreeEx(hProcess, myArg, sizeof(Struct), MEM_RELEASE);
    // Закрываем дескриптор процесса
    CloseHandle(hProcess);

Получаем:
b4aX9ze.png

Пример с WinAPI(Beep):

Создаем структуру и прототип:
C++:
typedef BOOL(__stdcall *__Beep)(DWORD, DWORD);
struct sBeep
{
    DWORD Freq;
    DWORD Duration;
    DWORD pAdr;

} myBeep;

Заполняем структуру:
C++:
myBeep.Freq = 500;
myBeep.Duration = 50;
HMODULE kernel = LoadLibrary("Kernel32.dll"); // Получаем адрес kernel32
myBeep.pAdr = (DWORD)GetProcAddress(kernel,"Beep"); // получаем адрес функции Beep
FreeLibrary(kernel);

C++:
DWORD __stdcall RemoteThread(sBeep *sArg)
{
    __Beep msg = (__Beep)sArg->pAdr;
    msg(sArg->Freq, sArg->Duration);
    return 0;
}
void __stdcall RemoteThread_end() {}

C++:
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcId("gta_sa.exe"));
    // Выделяем память под наш поток в gta_sa
    LPVOID pRemoteThread = VirtualAllocEx(hProcess, NULL, (DWORD_PTR)RemoteThread_end - (DWORD_PTR)RemoteThread, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    // Записываем его
    WriteProcessMemory(hProcess, pRemoteThread, (LPVOID)RemoteThread, ((DWORD_PTR)RemoteThread_end - (DWORD_PTR)RemoteThread), 0);
    // Выделяем память для нашего объекта в gta_sa
    sBeep *myArg = (sBeep*)VirtualAllocEx(hProcess, NULL, sizeof(sBeep), MEM_COMMIT, PAGE_READWRITE);
    // Записываем его
    WriteProcessMemory(hProcess, myArg, &myBeep, sizeof(sBeep), NULL);
    // Запускаем наш поток pRemoteThread с аргументами myArg
    HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)pRemoteThread, myArg, 0, 0);
    // Закрываем дескриптор потока
    CloseHandle(hThread);
    // Освобождаем выделенную память
    VirtualFreeEx(hProcess, myArg, sizeof(sBeep), MEM_RELEASE);
    // Закрываем дескриптор процесса
    CloseHandle(hProcess);
 
Последнее редактирование:

SR_team

like pancake
BH Team
4,707
6,347
а самое интересное не написал. Переключение процессов планировщиком происходит в момент вызова CreateRemoteThread или по истечению кванта? Т.е. поток выполняется сразу или отложено, при переключение процесса?
 

CleanLegend

Известный
Автор темы
Всефорумный модератор
474
927
а самое интересное не написал. Переключение процессов планировщиком происходит в момент вызова CreateRemoteThread или по истечению кванта? Т.е. поток выполняется сразу или отложено, при переключение процесса?
Сразу
 
  • Нравится
Реакции: SR_team

CleanLegend

Известный
Автор темы
Всефорумный модератор
474
927
Обновил. Изменил реализацию, добавил пример с WinAPI
 

#Rin

Известный
Всефорумный модератор
1,214
1,035
HMODULE kernel = LoadLibrary("Kernel32.dll"); // Получаем адрес kernel32 myBeep.pAdr = (DWORD)GetProcAddress(kernel,"Beep"); // получаем адрес функции Beep FreeLibrary(kernel);
Может windows и гарантирует загрузку такой системной библиотеки по одинаковому адресу. Но адрес библиотеки в разных процессах может и будет отличаться.
Поэтому что бы найти адрес функции относительно другого процесса, нужно:
Использовать CreateToolhelp32Snapshot, Module32First, Module32Next для нахождения адреса библиотеки в чужом процессе.
Затем загрузить туже библиотеку в свой процесс через LoadLibrary.
А после найти адрес функции относительно своего процесса через GetProcAddress.
Затем просто вычисляем: адрес_библиотеки_в_чужом процессе + адрес_функции_в_нашем_процессе - адрес_библиотеки_в_нашем_процессе.
 

#Rin

Известный
Всефорумный модератор
1,214
1,035
фича библиотек в том, что один и тот же код, один раз загруженный в память, используется всеми процессами. Различаются лишь данные
Я читал что в Линуксе, библиотека загруженная в память, используется всеми процессами.
Но в Виндоусе на каждый процесс свой экземпляр библиотеки.
 

SR_team

like pancake
BH Team
4,707
6,347
Я читал что в Линуксе, библиотека загруженная в память, используется всеми процессами.
Но в Виндоусе на каждый процесс свой экземпляр библиотеки.
1. нахуй они тогда нужны в винде?
2. Почему тогда фокус с получением адреса библиотечной функции из своего процесса работает?
 

#Rin

Известный
Всефорумный модератор
1,214
1,035
1. нахуй они тогда нужны в винде?
Спроси разработчиков виндовс. Если бы так было, то тот же АнтиСтилер ставил свои хуки не только внутри гта, а на весь комп в целом. А изменение кода в какой-то функции могло бы уронить систему.
2. Почему тогда фокус с получением адреса библиотечной функции из своего процесса работает?
Большинство библиотек (как правило системные) загружаются по одинаковому адресу, но виндовс не гарантирует этого.

На скрине ниже, можно заметить что у samp.dll отличаются базовые адреса в разных процессах.
upload_2019-5-1_22-57-9.png
 

SR_team

like pancake
BH Team
4,707
6,347
Спроси разработчиков виндовс. Если бы так было, то тот же АнтиСтилер ставил свои хуки не только внутри гта, а на весь комп в целом. А изменение кода в какой-то функции могло бы уронить систему.

Большинство библиотек (как правило системные) загружаются по одинаковому адресу, но виндовс не гарантирует этого.

На скрине ниже, можно заметить что у samp.dll отличаются базовые адреса в разных процессах.
Посмотреть вложение 29158
да ты прав. Кстати, что касаемо адресов, в PE можно указать желаемое адрессное пространство для библиотеки, и если оно не занято, то библиотека всегда будет грузится в него. По дефолту вроде адрес 0x10000000
 
  • Нравится
Реакции: #Rin

Vadim.dll

Известный
168
80
При попытке выполнения этого кода gta просто выключается, без ошибок, как будто завершается процесс
 

LUCHARE

Известный
Друг
545
687
круто конечно

жаль что баян (the CreateRemoteThread & WriteProcessMemory technique старше меня)
и перепизжено на разные форумы тысячу раз

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

Но адрес библиотеки в разных процессах может и будет отличаться.
Kernel32 is required to be at the same base address because there are a number of internal kernel32 routines that, similar to ntdll!DbgUiRemoteBreakIn, are used in cross-process thread injection. One example of this used to be the console control event handler In the case of console events, during kernel32.dll initialization, the address of the Ctrl-C event dispatcher is passed to WinSrv.dll (in CSRSS space).
...
By the time this change to WinSrv and Ctrl-C processing was made, though, the application compatibility impact of removing the kernel32 base address to be the same system-wide would have been too severe to eliminate the restriction (virtually all third party code injection code now relies heavily on this assumption). Thus, for this (and other) reasons, kernel32 still remains with the restriction that it may not be relocated to a different base address cross-process.
 

ntharbinger

Потрачен
13
36
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Проблемy с разным адресом процедуры из-за релокаций можно решить так
C++:
DWORD RVA = ((DWORD_PTR)GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateRemoteThread") - (DWORD_PTR)GetModuleHandleA("kernel32.dll"));
auto ObtainRemoteBase = [](HANDLE hProc, const char *dllName) -> DWORD_PTR
{
    HMODULE hMods[1024]; DWORD cbNeeded;
    EnumProcessModules(hProc, hMods, sizeof(hMods), &cbNeeded);
    for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
    {
        CHAR ModulePath[MAX_PATH + 1]; memset(ModulePath, 0, sizeof(ModulePath));
        K32GetModuleFileNameExA(hProc, hMods[i], ModulePath, MAX_PATH + 1);
        if (strstr(ModulePath, dllName) != nullptr)
        {
            return reinterpret_cast<DWORD_PTR>(hMods[i]);
        }
    }
};
auto FindProcessId = [](char* processName) -> DWORD
{
    char* p = strrchr(processName, '\\');
    if (p) processName = p + 1;
    PROCESSENTRY32 processInfo;
    processInfo.dwSize = sizeof(processInfo);
    HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    if (processesSnapshot == INVALID_HANDLE_VALUE) return 0;
    Process32First(processesSnapshot, &processInfo);
    if (!strcmp(processName, processInfo.szExeFile))
    {
        CloseHandle(processesSnapshot);
        return processInfo.th32ProcessID;
    }
    while (Process32Next(processesSnapshot, &processInfo))
    {
        if (!strcmp(processName, processInfo.szExeFile))
        {
            CloseHandle(processesSnapshot);
            return processInfo.th32ProcessID;
        }
    }
    CloseHandle(processesSnapshot);
    return 0;
};
DWORD procID = GetProcID("имя_процесса.ехе");
if (procID == NULL) return;
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procID);
if (hProc != NULL)
{
    DWORD_PTR funcAddr = RVA + ObtainRemoteBase(hProc, "kernel32.dll");
    CloseHandle(hProc);
}
 
Последнее редактирование: