Перейти к содержанию
Авторизация  
Xhayla

Page Guard Hooking на С++

Рекомендуемые сообщения

Всем привет.
Имеется приложение для теста Dll`ки, вот ее код.

Спойлер

#include "stdafx.h"
#include <windows.h>


int main()
{
	while (true)
	{
		MessageBox(0, L"TEST", 0, 0);
		Sleep(5000);
	}

    return 0;
}

 

 

 

Мне нужно изменить Sleep(5000) - 5 секунд на Speel(1000) к примеру.

 

Я нашел инструкцию в которой хранится значение "sleep(5000)", вот так выглядит(ТЫК)

За счет Dll`ки, я хочу перевести код на копию этого участка, где значение будет уже не 5000мс, а к примеру 1000мс.(для того чтобы понять, работает DLL или нет).

Вот код самой DLL

Спойлер

DWORD origAddress;
DWORD copyAddress;

LONG WINAPI HookPage(EXCEPTION_POINTERS *pExceptionInfo)
{
	if(pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
	{
		if (pExceptionInfo->ExceptionRecord->ExceptionAddress == (GetModuleHandle("ForHookMessage.exe") + 0x116F3))
		{
			pExceptionInfo->ContextRecord->Eip = (DWORD)copyAddress;

			return EXCEPTION_CONTINUE_EXECUTION;
		}
	}

	return EXCEPTION_CONTINUE_SEARCH;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	{

		origAddress = (DWORD)GetModuleHandle("ForHookMessage.exe") + 0x116F3; // 0x116F3 смещение до инструкции где хранится значение задежки Sleep, видно на скриншоте.

		MEMORY_BASIC_INFORMATION system_info = { 0 };
		VirtualQuery((LPCVOID)origAddress, &system_info, sizeof(MEMORY_BASIC_INFORMATION)); // получаю информацию об адрессе.

		copyAddress = (DWORD)malloc(system_info.RegionSize); // выделяю нужное кол-во байт памяти.

		memcpy((void*)copyAddress, reinterpret_cast<void*>(origAddress), system_info.RegionSize); // копирую оригинальную страницу в копию.

		AddVectoredExceptionHandler(1, HookPage); // устанавливаю отлов исключений.
		//SetUnhandledExceptionFilter(HookFunc);
		
		DWORD OldProt = 0;
		VirtualProtect((void*)origAddress, system_info.RegionSize, system_info.Protect | PAGE_GUARD, &OldProt); // изменяю в оригинальной странице защиту на PAGE_GUARD, чтобы вылезло исключение.
		break;
			
	}
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

 

 

 

Теперь, я делаю инжект этой DLL в процесс, а программа просто зависает. ЗАПИСАЛ ВИДЕО НА YOUTUBE

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

*(int*)( ((DWORD)GetModuleHandleA(NULL)) + 0x116F3 + 1 ) = 1000; Вроде все что необходимо
Что-то ты слишком много делаешь чтобы добиться такого простого результата

Изменено пользователем roma912

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
15 минут назад, roma912 сказал:

*(int*)( ((DWORD)GetModuleHandleA(NULL)) + 0x116F3 + 1 ) = 1000; Вроде все что необходимо
Что-то ты слишком много делаешь чтобы добиться такого простого результата

Я хочу понять, как делается PageGuard Hooking на C++, а это программа просто для проверка, работает или нет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
2 часа назад, Xhayla сказал:

Я хочу понять, как делается PageGuard Hooking на C++, а это программа просто для проверка, работает или нет.

По тому коду, который ты используешь оно даже работать не будет. Сейчас код напишу и закомментирую.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
28 минут назад, partoftheworlD сказал:

По тому коду, который ты используешь оно даже работать не будет. Сейчас код напишу и закомментирую.

@partoftheworlD, Жду, очень нужно, 2 день в тупике, сперва пробовал на игре, ничего не получалось, потом подумал, неужели вообще не работает, решил ТУПА попробовать на приложение где 3 строчки когда, ЦИКЛ - СООБЩЕНИЕ - ЗАДЕРЖКА, даже тут не получилось... Вы хорошо знаете C++?

Изменено пользователем Xhayla

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
26 минут назад, Xhayla сказал:

@partoftheworlD, Жду, очень нужно, 2 день в тупике, сперва пробовал на игре, ничего не получалось, потом подумал, неужели вообще не работает, решил ТУПА попробовать на приложение где 3 строчки когда, ЦИКЛ - СООБЩЕНИЕ - ЗАДЕРЖКА, даже тут не получилось... Вы хорошо знаете C++?

https://pastebin.com/4tXXM0av

 

 

 

 

Думаю подставить вместо &value адрес указывающий на копию куска памяти не составит труда.

 

26 минут назад, Xhayla сказал:

Вы хорошо знаете C++?

Нет

  • Плюс 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

@partoftheworlD, огромное спасибо, а откуда Вы взяли значение  INS_SIZE  = 10, почему 10? А еще, если я вместо value() укажу адрес указывающий на копию памяти, после того код ее выполнит, в конце нужно будет возвращаться на оригинал?

Изменено пользователем Xhayla

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
14 минут назад, Xhayla сказал:

а откуда Вы взяли значение  INS_SIZE  = 10, почему 10?

 

10 байт размер инструкций, но защита все равно поставится на весь регион памяти.

 

14 минут назад, Xhayla сказал:

в конце нужно будет возвращаться на оригинал?

Да, хотя не уверен, проблем не должно быть, ведь по идее ведь код одинаковый везде будет, но желательно вернуть управление оригинальному коду.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
1 минуту назад, partoftheworlD сказал:

Да

К примеру у меня есть участок скопированного кода, размером 300 байт, я перевожу код на копию, и в конце чтобы вернутся нужно к оригинальному адресу прибавить 300(размер копии оригинала) + 1(след инструкция), верно?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
22 минуты назад, Xhayla сказал:

верно?

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

Чтобы посчитать адрес возврата достаточно воспользоваться стеком, например так, сразу перед записью нового EIP.

jmp_original = (DWORD)(pExceptionInfo->ContextRecord->Esp);

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
26 минут назад, partoftheworlD сказал:

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

Чтобы посчитать адрес возврата достаточно воспользоваться стеком, например так, сразу перед записью нового EIP.


jmp_original = (DWORD)(pExceptionInfo->ContextRecord->Esp);

  

 и сразу после того как копия выполнится, прыгнуть в jml_original? Если к примеру у меня копия не 1 инструкция, а к примеру 1000 строк, если прыгнуть в jmp_original он все равно в правильный адрес прыгнет?

Как у Вас в видео, Вы сперва из 0x0042578C прыгаете на свою функцию, после возвращаетесь на 2 инструкцию после 0x0042578C, а если к примеру, прыгать из 0x0042578C и выполнять 5 инструкций, чтобы правильно приземлиться после хука, нужно jmp_original так вычислять - jpm_original = (DWORD)(pExceptionInfo->ContextRecord->Esp + 5; ? 

 

На счет защиты, как только мне перестанет быть нужен ХУК, нужно вернуть старую защиту и снять отлов исключений?

Изменено пользователем Xhayla

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
12 минут назад, Xhayla сказал:

а к примеру 1000 строк, если прыгнуть в jmp_original он все равно в правильный адрес прыгнет?

Хоть тысячу, хук работает как в CE, главное чтобы состояние измененных регистров не приводило к крашу.

12 минут назад, Xhayla сказал:

jpm_original = (DWORD)(pExceptionInfo->ContextRecord->Esp + 5; ? 

Это не так работает, советую посмотреть основы о том как работает стек.

 

12 минут назад, Xhayla сказал:

 

На счет защиты, как только мне перестанет быть нужен ХУК, нужно вернуть старую защиту и снять отлов исключений?

Да

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

@partoftheworlD, еще раз огромное спасибо Вам, все получилось, переделал когда под игру, изменил адрес прыжка и адрес возврата, так же код изменил на нужный, вот только тут проблема, игра стала зависать, fps упал до 30 и начал идти рывка, в меню игры все хорошо, но как только игра начинается, сразу начинает идти рывками что-ли, не связано ли это с тем, что PageGuard Hooking сам по себе медленный? Поскольку в виндовс на отлов исключений нужно какое-то время.

Изменено пользователем Xhayla

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
16 минут назад, Xhayla сказал:

PageGuard Hooking сам по себе медленный?

Да, но не настолько медленный, так же еще зависит от того сколько кода вторая часть обработчика проверяет(которая SINGLE STEP). Явно, если пошаговая отладка будет работать для 10000 инструкций в поиске той самой на которую повешен хук, то быстродействия ждать не стоит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
9 минут назад, partoftheworlD сказал:

Да, но не настолько медленный, так же еще зависит от того сколько кода вторая часть обработчика проверяет(которая SINGLE STEP).

Ну смотрите, я изменил INS_SIZE с 10 на 5.

А в value вписал следующий код - 

Спойлер

__declspec(naked) void value()
{
	__asm
	{
		xor eax,eax
		nop
		nop
		nop
		jmp to_addr
	}
}

 

Ну и конечно же поменял адреса начала прыжка и обратный прыжок, странно почему он так зависает.(Если что адрес прыжка состоит из 5 байт, поэтому в INS_SIZE вписал 5).

9 минут назад, partoftheworlD сказал:

Да, но не настолько медленный, так же еще зависит от того сколько кода вторая часть обработчика проверяет(которая SINGLE STEP). Явно, если пошаговая отладка будет работать для 10000 инструкций в поиске той самой на которую повешен хук, то быстродействия ждать не стоит.

То-есть, если выше адреса, который жду в исключении, будет очень много инструкций будет - зависать? Насколько я понимаю, там где вешается Protect: PAGE_GUARD он тоже будет отлавливаться, тогда как сделать так, чтобы VirtualProtect ставил PAGE_GUARD только на те кол-во байт, которые я указал, а не на всю страницу, можно ли с этим что-то сделать? Ведь в CE не виснет как тут, хотя он там тоже выставляет на всю страницу PAGE_GUARD.

Изменено пользователем Xhayla

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

@partoftheworlDя Вас уже замучал 😅, скажите пожалуйста. Как вписать в copyAddress, чтобы от туда в конце прыгать на оригинальный код, а именно на jmp_back.

Спойлер

DWORD origAddress = 0x6F361390; // оригинальный адресс
DWORD jmp_back = 0x6F361395; // адресс на который будем прыгать после прыжка на копию.
copyAddress = malloc(30); //выделяет к примеру 30 байт.
memcpy(copyAddress, origAddress, 5); // копируем 5 байт из оригинального адресса в выделенную память.

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
PBYTE copyAddress = (PBYTE) malloc(5); //выделяет к примеру 30 байт.
или BYTE *copyAddress = (BYTE *) malloc(5); //
или BYTE copyAddress[] = (BYTE *) malloc(5); // не точно, вроде может прокатить и без приведения типа. Но не помню, где квадратные скобки ставить.
memcpy((void *)copyAddress, origAddress, 5);

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
2 часа назад, Xipho сказал:

PBYTE copyAddress = (PBYTE) malloc(//выделяет к примеру 30 байт.
или BYTE *copyAddress = (BYTE *) malloc(//
BYTE copyAddress[] = (BYTE *) malloc(// не точно, вроде может прокатить и без приведения типа. Но не помню, где квадратные скобки ставить.memcpy5

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

Изменено пользователем Xhayla

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Расчитай прыжок с нужного места на нужный адрес и объедени (добавь в новый) свой массив байт с массивом байт прыжка

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Возможно, это подойдет. На счет VirtualProtect не уверен, но вроде он здесь нужен.

 

Спойлер

void copy_base(unsigned char* &copy_buffer, const MODULEINFO mi)
{
	DWORD dwOldProtect = 0;
	
	//получаем размер образа
	const auto size = mi.SizeOfImage;
	
	//копируем
	memcpy_s(copy_buffer, size, mi.lpBaseOfDll, size);

	VirtualProtect(copy_buffer, size, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	//добавляем jmp to_addr
	*reinterpret_cast<BYTE*>(copy_buffer[size])      = 0xE9;
	*reinterpret_cast<DWORD*>(copy_buffer[size + 1]) = to_addr;

	VirtualProtect(copy_buffer, size, dwOldProtect, 0);
}

void use_copy()
{
	//получений информации о модуле
	MODULEINFO mi;
	GetModuleInformation(0, 0, &mi, sizeof mi);
	
	// выделение памяти под копию c запасом в 5 байт
	// для записи jmp to_addr(прыжка в оригинал)
	auto buffer = new BYTE[mi.SizeOfImage + 5];
	//создание копии
	copy_base(buffer, mi);

	/*
	 *
	 *
	 * Какие-то действия с буфером
	 *
	 * 
	 */

	//Удаление копии
	delete[] buffer;
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
1 час назад, partoftheworlD сказал:

Возможно, это подойдет. На счет VirtualProtect не уверен, но вроде он здесь нужен.

 

  Показать контент


void copy_base(unsigned char* &copy_buffer, const MODULEINFO mi)
{
	DWORD dwOldProtect = 0;
	
	//получаем размер образа
	const auto size = mi.SizeOfImage;
	
	//копируем
	memcpy_s(copy_buffer, size, mi.lpBaseOfDll, size);

	VirtualProtect(copy_buffer, size, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	//добавляем jmp to_addr
	*reinterpret_cast<BYTE*>(copy_buffer[size])      = 0xE9;
	*reinterpret_cast<DWORD*>(copy_buffer[size + 1]) = to_addr;

	VirtualProtect(copy_buffer, size, dwOldProtect, 0);
}

void use_copy()
{
	//получений информации о модуле
	MODULEINFO mi;
	GetModuleInformation(0, 0, &mi, sizeof mi);
	
	// выделение памяти под копию c запасом в 5 байт
	// для записи jmp to_addr(прыжка в оригинал)
	auto buffer = new BYTE[mi.SizeOfImage + 5];
	//создание копии
	copy_base(buffer, mi);

	/*
	 *
	 *
	 * Какие-то действия с буфером
	 *
	 * 
	 */

	//Удаление копии
	delete[] buffer;
}

 

А хотя если подумать, есть ли смысл копировать из оригинального адреса байты в выделенную память, потом оттуда прыгать на оригинальный код, если Вы( @partoftheworlD ), показали другой способ.
 

Спойлер

__declspec(naked) void patch()
{
	__asm
	{
		xor eax,eax
		nop
		nop
		nop
		jmp to_addr
	}
}

 

Просто при срабатывании нужного хука, буду переводить туда оригинальный код. pExceptionInfo->ContextRecord->Eip = reinterpret_cast<DWORD>(&patch);
Вот только странно, почему в CE когда изменяешь байты через StealthEdit, игры не зависает и не идет рывками, а через PugaGuard на С++ наоборот, зависает. Хотя и там и там тот же адрес хукается.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
39 минут назад, Xhayla сказал:

Вот только странно, почему в CE когда изменяешь байты через StealthEdit, игры не зависает и не идет рывками, а через PugaGuard на С++ наоборот, зависает. Хотя и там и там тот же адрес хукается.

 

Да, это странно, потому что, вчера когда писал пример проверял на скайриме никаких проблем не было, фпс не просаживал, все так же плавно было. Могу предположить, что проблема в алгоритме, либо в самой игре, возможно у неё есть собственный обработчик исключений, который что-то с ними делает. И наверное было бы лучше просто убить эту защиту.

 

39 минут назад, Xhayla сказал:

показали другой способ.

 

Способ с asm вставками не будет работать если игра x64

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
6 минут назад, partoftheworlD сказал:

 

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

Игра называется Warcraft 3 TFT, возможно в CE StealthEdit это реализовано по другому.

Изменено пользователем Xhayla

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
17 минут назад, partoftheworlD сказал:

Способ с asm вставками не будет работать если игра x64

код который Вы скинули мне выше, это для того чтобы с копии прыгать в нужный мне адрес?

2 часа назад, partoftheworlD сказал:

Возможно, это подойдет. На счет VirtualProtect не уверен, но вроде он здесь нужен.

 

  Показать контент


void copy_base(unsigned char* &copy_buffer, const MODULEINFO mi)
{
	DWORD dwOldProtect = 0;
	
	//получаем размер образа
	const auto size = mi.SizeOfImage;
	
	//копируем
	memcpy_s(copy_buffer, size, mi.lpBaseOfDll, size);

	VirtualProtect(copy_buffer, size, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	//добавляем jmp to_addr
	*reinterpret_cast<BYTE*>(copy_buffer[size])      = 0xE9;
	*reinterpret_cast<DWORD*>(copy_buffer[size + 1]) = to_addr;

	VirtualProtect(copy_buffer, size, dwOldProtect, 0);
}

void use_copy()
{
	//получений информации о модуле
	MODULEINFO mi;
	GetModuleInformation(0, 0, &mi, sizeof mi);
	
	// выделение памяти под копию c запасом в 5 байт
	// для записи jmp to_addr(прыжка в оригинал)
	auto buffer = new BYTE[mi.SizeOfImage + 5];
	//создание копии
	copy_base(buffer, mi);

	/*
	 *
	 *
	 * Какие-то действия с буфером
	 *
	 * 
	 */

	//Удаление копии
	delete[] buffer;
}

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
51 минуту назад, partoftheworlD сказал:

 

Да, это странно, потому что, вчера когда писал пример проверял на скайриме никаких проблем не было, фпс не просаживал, все так же плавно было. Могу предположить, что проблема в алгоритме, либо в самой игре, возможно у неё есть собственный обработчик исключений, который что-то с ними делает. И наверное было бы лучше просто убить эту защиту.

 

 

Способ с asm вставками не будет работать если игра x64

Пробовал с помощью кода выше с копии прыгнуть обратно на оригинал, не получилось. (У меня:) )

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
Авторизация  

×

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.