Introduction
- Overview of the problem
- Basic concepts of calling functions in a remote process

Calling the ChangeWindowTreeProtection function

- Overview of the ChangeWindowTreeProtection function
- Techniques for calling the function in a remote process
- Using the assembly code stub technique
- Using the CreateRemoteThread function

Creating our program
- Creating a remote process using CreateProcess
- Allocating memory in the target process using VirtualAllocEx
- Writing assembly code to the allocated memory using WriteProcessMemory
- Modifying the target function to jump to the allocated memory
- Calling a remote function using the CallRemoteFunction function
- Handling errors and exceptions

Summary of key concepts and techniques
- Final thoughts and recommendations

Прежде чем мы двинемся дальше, обязательно: я хочу уточнить, что любые предпринятые действия были исключительно в образовательных целях и для понимания того, что на данный момент достижимо. Моя цель состояла в том, чтобы расширить свои знания и лучше понять, что возможно и что невозможно в теоретическом смысле. Все эксперименты я проводил в контролируемой тестовой лаборатории на своем личном оборудовании. Я надеюсь, что, поделившись этими знаниями с сообществом, другие смогут извлечь уроки из моего опыта и лучше понять теоретические возможности в этой области.

Давай начнем!

Сценарий вызова функции ChangeWindowTreeProtection в качестве обхода SetWindowDisplayAffinity, мы используем следующие шаги на C++:

  1. Загрузите библиотеку win32kfull с помощью функции LoadLibrary.
  2. Получите указатель на функцию ChangeWindowTreeProtection с помощью функции GetProcAddress.
  3. Вызовите функцию ChangeWindowTreeProtection с соответствующими параметрами, чтобы установить уровень защиты окна.

Вот фрагмент кода, демонстрирующий использование функции ChangeWindowTreeProtection:

#include <Windows.h>

int main()
{
    // Load the win32kfull library
    HMODULE hWin32K = LoadLibrary(L"win32kfull.dll");
    
    if (hWin32K == NULL)
    {
        // Handle the error
        return 1;
    }
    
    // Get a pointer to the ChangeWindowTreeProtection function
    typedef BOOL(WINAPI* PChangeWindowTreeProtection)(HWND hWnd, DWORD flags);
    PChangeWindowTreeProtection pChangeWindowTreeProtection =
        (PChangeWindowTreeProtection)GetProcAddress(hWin32K, "ChangeWindowTreeProtection");

    if (pChangeWindowTreeProtection == NULL)
    {
        // Handle the error
        FreeLibrary(hWin32K);
        return 1;
    }
    
    // Call the ChangeWindowTreeProtection function
    HWND hWnd = GetForegroundWindow(); // Get the handle to the active window
    DWORD flags = 0x00000001; // Set the protection level to PROTECTED
    BOOL result = pChangeWindowTreeProtection(hWnd, flags);
    
    if (!result)
    {
        // Handle the error
        FreeLibrary(hWin32K);
        return 1;
    }
    
    // Free the library handle
    FreeLibrary(hWin32K);
    
    return 0;
}

Затем я хочу, чтобы код возвращал кодкейв WDA-NONE (обход черного экрана) в окнах на уровне API. Решение включает в себя написание небольшой заглушки ассемблерного кода, которая будет выполнять нашу функцию и возвращать желаемое значение. Затем эта заглушка может быть внедрена в наш целевой процесс в качестве кодовой пещеры с использованием внедрения DLL или очистки процесса.

; Assembly code to return WDA_NONE (0) from a codecave

push 0 ; Push the value of WDA_NONE onto the stack
ret ; Return from the function

Этот код просто помещает значение WDA_NONE (которое определено как 0) в стек, а затем возвращает функцию. Когда этот код выполняется, целевой процесс фактически «увидит» вызов нужной функции, которая возвращает желаемое значение.

Чтобы внедрить этот код как кодовую пещеру, мы делаем следующие шаги:

  1. Откройте целевой процесс с помощью функции OpenProcess.
  2. Выделите блок памяти в целевом процессе с помощью функции VirtualAllocEx. Этот блок памяти будет служить кодовой пещерой.
  3. Запишите заглушку ассемблерного кода в выделенную память с помощью функции WriteProcessMemory.
  4. Измените целевую функцию, чтобы перейти к кодовой пещере вместо выполнения исходного кода. Это можно сделать, перезаписав первые несколько байтов целевой функции командой перехода, указывающей на адрес кодовой пещеры.
  5. Выполнить целевую функцию. Инструкция перехода перенаправит поток выполнения в кодовую пещеру, которая вернет желаемое значение.

Вот пример фрагмента кода C++, который демонстрирует внедрение заглушки ассемблерного кода в качестве codecave:

#include <Windows.h>

// Define the assembly code stub
BYTE code[] = { 0x6A, 0x00, 0xC3 }; // Push 0 onto the stack, then return

int main()
{
    // Open the target process
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, <processId>);
    
    if (hProcess == NULL)
    {
        // Handle the error
        return 1;
    }
    
    // Allocate a block of memory in the target process
    LPVOID pCodecave = VirtualAllocEx(hProcess, NULL, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    
    if (pCodecave == NULL)
    {
        // Handle the error
        CloseHandle(hProcess);
        return 1;
    }
    
    // Write the assembly code stub to the allocated memory
    SIZE_T bytesWritten = 0;
    BOOL result = WriteProcessMemory(hProcess, pCodecave, code, sizeof(code), &bytesWritten);
    
    if (!result || bytesWritten != sizeof(code))
    {
        // Handle the error
        VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
        CloseHandle(hProcess);
        return 1;
    }
    
    // Modify the target function to jump to the codecave
    LPVOID pTargetFunction = <address of the target function>;
    BYTE jump[] = { 0xE9, 0x00, 0x00, 0x00, 0x00 }; // Jump instruction
    DWORD relativeAddress = (DWORD)pCodecave - ((DWORD)pTargetFunction + sizeof(jump));
    *(DWORD*)(jump + 1) = relativeAddress; // Calculate the relative address of the codecave
    bytesWritten = 0;
    result = WriteProcessMemory(hProcess, pTargetFunction, jump, sizeof(jump), &bytesWritten);
    
    if (!result || bytesWritten != sizeof(jump))
{
    // Handle the error
    VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return 1;
}

// Execute the target function
DWORD returnValue = 0;
result = CallRemoteFunction(hProcess, pTargetFunction, &returnValue);

if (!result)
{
    // Handle the error
    VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return 1;
}

// Free the allocated memory and close the process handle
VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
CloseHandle(hProcess);

// Handle the return value as necessary
return 0;

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

Здесь я реализую CallRemoteFunction.

#include <Windows.h>
#include <TlHelp32.h>

// Define the assembly code stub
BYTE code[] = { 0x6A, 0x00, 0xC3 }; // Push 0 onto the stack, then return

// Function to call a remote function in a target process
BOOL CallRemoteFunction(HANDLE hProcess, LPVOID pFunction, PDWORD pReturnValue)
{
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunction, NULL, 0, NULL);
    
    if (hThread == NULL)
    {
        return FALSE;
    }
    
    WaitForSingleObject(hThread, INFINITE);
    GetExitCodeThread(hThread, pReturnValue);
    CloseHandle(hThread);
    
    return TRUE;
}

// Function to get the process ID of a running process
DWORD GetProcessIdByName(const WCHAR* processName)
{
    DWORD processId = 0;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
    
    if (Process32First(hSnapshot, &processEntry))
    {
        do
        {
            if (_wcsicmp(processEntry.szExeFile, processName) == 0)
            {
                processId = processEntry.th32ProcessID;
                break;
            }
        } while (Process32Next(hSnapshot, &processEntry));
    }
    
    CloseHandle(hSnapshot);
    
    return processId;
}

int main()
{
    // Get the process ID of the target process
    DWORD processId = GetProcessIdByName(L"target.exe");
    
    if (processId == 0)
    {
        // Handle the error
        return 1;
    }
    
    // Open the target process
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
    
    if (hProcess == NULL)
    {
        // Handle the error
        return 1;
    }
    
    // Allocate a block of memory in the target process
    LPVOID pCodecave = VirtualAllocEx(hProcess, NULL, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    
    if (pCodecave == NULL)
    {
        // Handle the error
        CloseHandle(hProcess);
        return 1;
    }
    
    // Write the assembly code stub to the allocated memory
    SIZE_T bytesWritten = 0;
    BOOL result = WriteProcessMemory(hProcess, pCodecave, code, sizeof(code), &bytesWritten);
    
    if (!result || bytesWritten != sizeof(code))
    {
        // Handle the error
        VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
        CloseHandle(hProcess);
        return 1;
    }
    
    // Modify the target function to jump to the codecave
    LPVOID pTargetFunction = <address of the target function>;
    BYTE jump[] = { 0xE9, 0x00, 0x00, 0x00, 0x00 }; // Jump instruction
    DWORD relativeAddress = (DWORD)pCodecave - ((DWORD)pTargetFunction + sizeof(jump));
    *(DWORD*)(jump + 1) = relativeAddress; // Calculate the relative address of the codecave
    bytesWritten = 0;
    result = WriteProcessMemory(hProcess, pTargetFunction, jump, sizeof(jump), &bytesWritten);
    
    if (!result || bytesWritten != sizeof(jump))
{
    // Handle the error
    VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return 1;
}

// Execute the target function
DWORD returnValue = 0;
result = CallRemoteFunction(hProcess, pTargetFunction, &returnValue);

if (!result)
{
    // Handle the error
    VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return 1;
}

// Free the allocated memory and close the process handle
VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
CloseHandle(hProcess);

// Handle the return value as necessary
return 0;

Обратите внимание, что функция GetProcessIdByName используется для получения идентификатора целевого процесса. Эта функция принимает имя целевого процесса в качестве параметра и возвращает соответствующий идентификатор процесса. Эта функция использует функции Windows API `CreateToolhelp32Snapshot`, `Process32First` и `Process32Next` для перебора запущенных процессов и поиска целевого процесса.

Кроме того, `‹адрес целевой функции›` в строке 47 следует заменить фактическим адресом целевой функции в целевом процессе. Конкретный адрес зависит от целевой функции, поэтому вам нужно будет найти его с помощью отладчика или других средств.

Вот пример использования WinDbg для поиска адреса функции в целевом процессе:

  1. Откройте WinDbg и выберите Файл › Присоединить к процессу.
  2. Выберите целевой процесс из списка и нажмите OK.
  3. В командном окне введите sxe ld, чтобы прервать загрузку модуля.
  4. Введите g, чтобы продолжить выполнение целевого процесса, пока не будет загружен новый модуль.
  5. Введите lm, чтобы вывести список всех загруженных модулей.
  6. Найдите модуль, содержащий целевую функцию, и запишите его базовый адрес.
  7. Введите x <module base>+<offset>, чтобы разобрать модуль и найти адрес целевой функции.
  8. Замените <address of the target function> в коде фактическим адресом целевой функции.

Но кто хочет это сделать? Мы можем сделать это с помощью кода.

Функция для получения адреса функции

#include <Windows.h>
#include <Psapi.h>

// Function to get the address of a function in a module
LPVOID GetFunctionAddress(HMODULE hModule, const char* functionName)
{
    // Get the export directory of the module
    PIMAGE_NT_HEADERS pNTHeader = ImageNtHeader(hModule);
    PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)hModule + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    
    // Get the array of function addresses and names
    PDWORD pFunctionAddresses = (PDWORD)((LPBYTE)hModule + pExportDirectory->AddressOfFunctions);
    PDWORD pFunctionNames = (PDWORD)((LPBYTE)hModule + pExportDirectory->AddressOfNames);
    PWORD pFunctionNameOrdinals = (PWORD)((LPBYTE)hModule + pExportDirectory->AddressOfNameOrdinals);
    
    // Find the index of the function name in the name array
    DWORD index = (DWORD)-1;
    
    for (DWORD i = 0; i < pExportDirectory->NumberOfNames; i++)
    {
        const char* pFunctionName = (const char*)((LPBYTE)hModule + pFunctionNames[i]);
        
        if (_stricmp(pFunctionName, functionName) == 0)
        {
            index = i;
            break;
        }
    }
    
    if (index == (DWORD)-1)
    {
        return NULL;
    }
    
    // Get the address of the function
    DWORD functionAddress = pFunctionAddresses[pFunctionNameOrdinals[index]];
    LPVOID pFunction = (LPVOID)((LPBYTE)hModule + functionAddress);
    
    return pFunction;
}

// Function to get the module handle of a running process by its name
HMODULE GetModuleHandleByName(DWORD processId, const WCHAR* moduleName)
{
    HMODULE hModule = NULL;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId);
    MODULEENTRY32 moduleEntry = { sizeof(MODULEENTRY32) };
    
    if (Module32First(hSnapshot, &moduleEntry))
    {
        do
        {
            if (_wcsicmp(moduleEntry.szModule, moduleName) == 0)
            {
                hModule = moduleEntry.hModule;
                break;
            }
        } while (Module32Next(hSnapshot, &moduleEntry));
    }
    
    CloseHandle(hSnapshot);
    
    return hModule;
}

int main()
{
    // Get the process ID of the target process by window name
    HWND hWnd = FindWindow(NULL, L"target window name");
    
    if (hWnd == NULL)
    {
        // Handle the error
        return 1;
    }
    
    DWORD processId = 0;
    GetWindowThreadProcessId(hWnd, &processId);
    
    if (processId == 0)
    {
        // Handle the error
        return 1;
    }
    
    // Get the module handle of the target module by name
    HMODULE hModule = GetModuleHandleByName(processId, L"target.dll");
    
    if (hModule == NULL)
    {
        // Handle the error
        return 1;
    }
    
    // Get the address of the target function by name
    const char* functionName = "targetFunction";
    LPVOID pTargetFunction = GetFunctionAddress(hModule, functionName);

if (pTargetFunction == NULL)
{
    // Handle the error
    return 1;
}

// Open the target process
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);

if (hProcess == NULL)
{
    // Handle the error
    return 1;
}

// Allocate a block of memory in the target process
LPVOID pCodecave = VirtualAllocEx(hProcess, NULL, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

if (pCodecave == NULL)
{
    // Handle the error
    CloseHandle(hProcess);
    return 1;
}

// Write the assembly code stub to the allocated memory
SIZE_T bytesWritten = 0;
BOOL result = WriteProcessMemory(hProcess, pCodecave, code, sizeof(code), &bytesWritten);

if (!result || bytesWritten != sizeof(code))
{
    // Handle the error
    VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return 1;
}

// Modify the target function to jump to the codecave
BYTE jump[] = { 0xE9, 0x00, 0x00, 0x00, 0x00 }; // Jump instruction
DWORD relativeAddress = (DWORD)pCodecave - ((DWORD)pTargetFunction + sizeof(jump));
*(DWORD*)(jump + 1) = relativeAddress; // Calculate the relative address of the codecave
bytesWritten = 0;
result = WriteProcessMemory(hProcess, pTargetFunction, jump, sizeof(jump), &bytesWritten);

if (!result || bytesWritten != sizeof(jump))
{
    // Handle the error
    VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return 1;
}

// Execute the target function
DWORD returnValue = 0;
result = CallRemoteFunction(hProcess, pTargetFunction, &returnValue);

if (!result)
{
    // Handle the error
    VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return 1;
}

// Free the allocated memory and close the process handle
VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
CloseHandle(hProcess);

// Handle the return value as necessary
return 0;

Этот код использует функцию GetWindowThreadProcessId для получения идентификатора целевого процесса по имени окна, функцию GetModuleHandleByName для получения дескриптора целевого модуля по имени и функцию GetFunctionAddress для получения адреса окна. целевая функция по имени.

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

Собираем все вместе

вот пошаговое руководство по использованию предоставленного мной кода C++ для вызова функции ChangeWindowTreeProtection из win32kfull в тестовой среде:

  1. Откройте новый проект Visual Studio и создайте два исходных файла с именами main.cpp и RemoteFunctionCall.cpp.
  2. Скопируйте и вставьте следующий код в main.cpp:
#include <Windows.h>

// Define the assembly code stub
BYTE code[] = { 0x6A, 0x00, 0xC3 }; // Push 0 onto the stack, then return

// Function to call a remote function in a target process
bool CallRemoteFunction(HANDLE hProcess, LPVOID pFunction, DWORD* pReturnValue)
{
    // Create a remote thread in the target process to call the function
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunction, NULL, 0, NULL);
    
    if (hThread == NULL)
    {
        return false;
    }
    
    // Wait for the thread to exit
    DWORD result = WaitForSingleObject(hThread, INFINITE);
    
    if (result != WAIT_OBJECT_0)
    {
        CloseHandle(hThread);
        return false;
    }
    
    // Get the return value of the function
    result = GetExitCodeThread(hThread, pReturnValue);
    
    if (result == 0)
    {
        CloseHandle(hThread);
        return false;
    }
    
    // Close the thread handle
    CloseHandle(hThread);
    
    return true;
}

int main()
{
    // Get the process ID of the target process by window name
    HWND hWnd = FindWindow(NULL, L"test window");
    
    if (hWnd == NULL)
    {
        // Handle the error
        return 1;
    }
    
    DWORD processId = 0;
    GetWindowThreadProcessId(hWnd, &processId);
    
    if (processId == 0)
    {
        // Handle the error
        return 1;
    }
    
    // Get the module handle of win32kfull.dll
    HMODULE hModule = GetModuleHandle(L"win32kfull.dll");
    
    if (hModule == NULL)
    {
        // Handle the error
        return 1;
    }
    
    // Get the address of the ChangeWindowTreeProtection function
    LPVOID pFunction = GetProcAddress(hModule, "ChangeWindowTreeProtection");
    
    if (pFunction == NULL)
    {
        // Handle the error
        return 1;
    }
    
    // Open the target process
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
    
    if (hProcess == NULL)
    {
        // Handle the error
        return 1;
    }
    
    // Allocate a block of memory in the target process
    LPVOID pCodecave = VirtualAllocEx(hProcess, NULL, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    
    if (pCodecave == NULL)
    {
        // Handle the error
        CloseHandle(hProcess);
        return 1;
    }
    
    // Write the assembly code stub to the allocated memory
    SIZE_T bytesWritten = 0;
    BOOL result = WriteProcessMemory(hProcess, pCodecave, code, sizeof(code), &bytesWritten);
    
    if (!result || bytesWritten != sizeof(code))
    {
        // Handle the error
        VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
        CloseHandle(hProcess);
        return 1;
    }
    
    // Modify the target function to jump to the codecave
BYTE jump[] = { 0xE9, 0x00, 0x00, 0x00, 0x00 }; // Jump instruction
DWORD relativeAddress = (DWORD)pCodecave - ((DWORD)pFunction + sizeof(jump));
*(DWORD*)(jump + 1) = relativeAddress; // Calculate the relative address of the codecave
bytesWritten = 0;
result = WriteProcessMemory(hProcess, pFunction, jump, sizeof(jump), &bytesWritten);

if (!result || bytesWritten != sizeof(jump))
{
    // Handle the error
    VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return 1;
}

// Execute the target function
DWORD returnValue = 0;
result = CallRemoteFunction(hProcess, pFunction, &returnValue);

if (!result)
{
    // Handle the error
    VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return 1;
}

// Free the allocated memory and close the process handle
VirtualFreeEx(hProcess, pCodecave, 0, MEM_RELEASE);
CloseHandle(hProcess);

// Handle the return value as necessary
return 0;

3. Скопируйте и вставьте следующий код в `RemoteFunctionCall.cpp`:

#include <Windows.h>

// Function to call a remote function in a target process
bool CallRemoteFunction(HANDLE hProcess, LPVOID pFunction, DWORD* pReturnValue)
{
// Create a remote thread in the target process to call the function
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunction, NULL, 0, NULL);

if (hThread == NULL)
{
    return false;
}

// Wait for the thread to exit
DWORD result = WaitForSingleObject(hThread, INFINITE);

if (result != WAIT_OBJECT_0)
{
    CloseHandle(hThread);
    return false;
}

// Get the return value of the function
result = GetExitCodeThread(hThread, pReturnValue);

if (result == 0)
{
    CloseHandle(hThread);
    return false;
}

// Close the thread handle
CloseHandle(hThread);

return true;

4. Соберите проект и запустите исполняемый файл. Не забудьте заменить «тестовое окно» именем окна, на которое вы хотите настроить таргетинг, и при необходимости скорректируйте заглушку ассемблерного кода.

  1. После запуска исполняемый файл попытается найти окно с указанным именем и получить идентификатор целевого процесса. Если ему не удастся найти окно, он вернет ошибку и завершит работу.
  2. Затем исполняемый файл попытается получить дескриптор модуля win32kfull.dll и адрес функции ChangeWindowTreeProtection. Если какая-либо из этих операций завершится ошибкой, она вернет ошибку и завершит работу.
  3. Затем исполняемый файл выделит блок памяти в целевом процессе, запишет заглушку ассемблерного кода в выделенную память и изменит функцию ChangeWindowTreeProtection для перехода в кодовую пещеру. Если какая-либо из этих операций завершится неудачно, она вернет ошибку и завершит работу.
  4. Наконец, исполняемый файл выполнит функцию ChangeWindowTreeProtection в целевом процессе, вызвав ее удаленно, используя функцию CallRemoteFunction. Если удаленный вызов функции завершится ошибкой, он вернет ошибку и завершит работу.
  5. Если все операции выполнены успешно, исполняемый файл освободит выделенную память и закроет дескриптор процесса перед выходом.

Обратите внимание, что этот код предполагает, что функция ChangeWindowTreeProtection может быть успешно вызвана с помощью заглушки ассемблерного кода. Если это не так, вам может потребоваться изменить ассемблерный код или использовать другой метод для вызова функции (см. ниже).

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

  1. Замените следующий код в main.cpp:
// Define the assembly code stub
BYTE code[] = { 0x6A, 0x00, 0xC3 }; // Push 0 onto the stack, then return

с

// Define the function parameters
BOOL enable = TRUE;
DWORD protect;

// Get the address of the ChangeWindowTreeProtection function
HMODULE hModule = GetModuleHandle(L"win32kfull.dll");

if (hModule == NULL)
{
    // Handle the error
    return 1;
}

LPVOID pFunction = GetProcAddress(hModule, "ChangeWindowTreeProtection");

if (pFunction == NULL)
{
    // Handle the error
    return 1;
}

// Call the ChangeWindowTreeProtection function using CreateRemoteThread
DWORD threadId = 0;
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunction, &enable, 0, &threadId);

if (hThread == NULL)
{
    // Handle the error
    return 1;
}

// Wait for the thread to exit
DWORD result = WaitForSingleObject(hThread, INFINITE);

if (result != WAIT_OBJECT_0)
{
    // Handle the error
    CloseHandle(hThread);
    return 1;
}

// Get the return value of the function
result = GetExitCodeThread(hThread, &protect);

if (result == 0)
{
    // Handle the error
    CloseHandle(hThread);
    return 1;
}

// Close the thread handle
CloseHandle(hThread);
  1. Удалите функцию CallRemoteFunction из main.cpp и RemoteFunctionCall.cpp.
  2. Соберите проект и запустите исполняемый файл. Измененный код вызовет функцию ChangeWindowTreeProtection, используя CreateRemoteThread вместо заглушки ассемблерного кода.

В этой статье мы рассмотрели, как вызвать функцию ChangeWindowTreeProtection в целевом процессе с помощью C++. Эта функция является частью библиотеки win32kfull.dll в операционной системе Windows и используется для защиты дерева окон данного окна.

Мы начали с обсуждения основных понятий, лежащих в основе вызова функций в удаленном процессе, и обрисовали в общих чертах некоторые проблемы и соображения, связанные с этим процессом. Затем мы перешли к особенностям вызова функции ChangeWindowTreeProtection и обсудили несколько различных способов достижения этого, в том числе использование заглушки ассемблерного кода и использование функции CreateRemoteThread.

Затем мы предоставили пошаговое руководство по реализации программы на C++, которая вызывает функцию ChangeWindowTreeProtection с использованием техники заглушки ассемблерного кода. В это руководство включены подробные примеры кода для создания удаленного процесса, выделения памяти в целевом процессе, записи ассемблерного кода в выделенную память и модификации целевой функции для перехода к выделенной памяти. Мы также предоставили отдельный пример кода для вызова удаленной функции с помощью функции CreateRemoteThread.