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++:
- Загрузите библиотеку win32kfull с помощью функции LoadLibrary.
- Получите указатель на функцию ChangeWindowTreeProtection с помощью функции GetProcAddress.
- Вызовите функцию 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) в стек, а затем возвращает функцию. Когда этот код выполняется, целевой процесс фактически «увидит» вызов нужной функции, которая возвращает желаемое значение.
Чтобы внедрить этот код как кодовую пещеру, мы делаем следующие шаги:
- Откройте целевой процесс с помощью функции OpenProcess.
- Выделите блок памяти в целевом процессе с помощью функции VirtualAllocEx. Этот блок памяти будет служить кодовой пещерой.
- Запишите заглушку ассемблерного кода в выделенную память с помощью функции WriteProcessMemory.
- Измените целевую функцию, чтобы перейти к кодовой пещере вместо выполнения исходного кода. Это можно сделать, перезаписав первые несколько байтов целевой функции командой перехода, указывающей на адрес кодовой пещеры.
- Выполнить целевую функцию. Инструкция перехода перенаправит поток выполнения в кодовую пещеру, которая вернет желаемое значение.
Вот пример фрагмента кода 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 для поиска адреса функции в целевом процессе:
- Откройте WinDbg и выберите Файл › Присоединить к процессу.
- Выберите целевой процесс из списка и нажмите OK.
- В командном окне введите
sxe ld
, чтобы прервать загрузку модуля. - Введите
g
, чтобы продолжить выполнение целевого процесса, пока не будет загружен новый модуль. - Введите
lm
, чтобы вывести список всех загруженных модулей. - Найдите модуль, содержащий целевую функцию, и запишите его базовый адрес.
- Введите
x <module base>+<offset>
, чтобы разобрать модуль и найти адрес целевой функции. - Замените
<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
в тестовой среде:
- Откройте новый проект Visual Studio и создайте два исходных файла с именами
main.cpp
иRemoteFunctionCall.cpp
. - Скопируйте и вставьте следующий код в
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. Соберите проект и запустите исполняемый файл. Не забудьте заменить «тестовое окно» именем окна, на которое вы хотите настроить таргетинг, и при необходимости скорректируйте заглушку ассемблерного кода.
- После запуска исполняемый файл попытается найти окно с указанным именем и получить идентификатор целевого процесса. Если ему не удастся найти окно, он вернет ошибку и завершит работу.
- Затем исполняемый файл попытается получить дескриптор модуля
win32kfull.dll
и адрес функцииChangeWindowTreeProtection
. Если какая-либо из этих операций завершится ошибкой, она вернет ошибку и завершит работу. - Затем исполняемый файл выделит блок памяти в целевом процессе, запишет заглушку ассемблерного кода в выделенную память и изменит функцию
ChangeWindowTreeProtection
для перехода в кодовую пещеру. Если какая-либо из этих операций завершится неудачно, она вернет ошибку и завершит работу. - Наконец, исполняемый файл выполнит функцию
ChangeWindowTreeProtection
в целевом процессе, вызвав ее удаленно, используя функциюCallRemoteFunction
. Если удаленный вызов функции завершится ошибкой, он вернет ошибку и завершит работу. - Если все операции выполнены успешно, исполняемый файл освободит выделенную память и закроет дескриптор процесса перед выходом.
Обратите внимание, что этот код предполагает, что функция ChangeWindowTreeProtection
может быть успешно вызвана с помощью заглушки ассемблерного кода. Если это не так, вам может потребоваться изменить ассемблерный код или использовать другой метод для вызова функции (см. ниже).
Если заглушка ассемблерного кода не работает для вызова функции ChangeWindowTreeProtection
, вы можете попробовать другой метод, например, использовать функцию CreateRemoteThread
для создания удаленного потока в целевом процессе, который будет вызывать функцию. Вот пример того, как изменить код, чтобы использовать эту технику:
- Замените следующий код в
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);
- Удалите функцию
CallRemoteFunction
изmain.cpp
иRemoteFunctionCall.cpp
. - Соберите проект и запустите исполняемый файл. Измененный код вызовет функцию
ChangeWindowTreeProtection
, используяCreateRemoteThread
вместо заглушки ассемблерного кода.
В этой статье мы рассмотрели, как вызвать функцию ChangeWindowTreeProtection
в целевом процессе с помощью C++. Эта функция является частью библиотеки win32kfull.dll
в операционной системе Windows и используется для защиты дерева окон данного окна.
Мы начали с обсуждения основных понятий, лежащих в основе вызова функций в удаленном процессе, и обрисовали в общих чертах некоторые проблемы и соображения, связанные с этим процессом. Затем мы перешли к особенностям вызова функции ChangeWindowTreeProtection
и обсудили несколько различных способов достижения этого, в том числе использование заглушки ассемблерного кода и использование функции CreateRemoteThread
.
Затем мы предоставили пошаговое руководство по реализации программы на C++, которая вызывает функцию ChangeWindowTreeProtection
с использованием техники заглушки ассемблерного кода. В это руководство включены подробные примеры кода для создания удаленного процесса, выделения памяти в целевом процессе, записи ассемблерного кода в выделенную память и модификации целевой функции для перехода к выделенной памяти. Мы также предоставили отдельный пример кода для вызова удаленной функции с помощью функции CreateRemoteThread
.