Перейти к содержанию

WaitForDebugEvent(...) не отлавливает DEBUG_EVENT


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

Нужно сделать подобие отладчика на C++.

Не могу понять почему WaitForDebugEvent(...) не отлавливает DEBUG_EVENT.

Как я это обнаружил? 

Я выполняю пошаговую отладку в Visual Studio, когда попадаю в функцию WaitForDebugEvent(...), выполнение программы замораживается и всё. Стоит на этом WaitForDebugEvent(...) и дальше пошагово не могу идти по коду.

Что я не так делаю? Делал через аппаратный брейкпоинт, по уроку Аппаратный перехват функций от канала Dmitry K. на YouTube.

Хотелось  бы через int 3 сделать, но когда я записываю 0xCC с нопами по адресу, где сидит найденная сигнатура, то программа вылетает.

Отлаживаемая программа: Tutorial-i386.exe Step 2. Инструкция берётся, записывающая по адресу при нажатии кнопки Hit me. Вот мне нужно получить значение регистра ebx. В принципе понятно, что это надо делать через контекст, но помогите написать отладчик, пожалуйста! Мне нужно именно на C++.

Вот код:

Спойлер

proc.GetProcessByName("Tutorial-i386.exe");
            proc.Open();
            MODULEENTRY32 game = { 0 };
            proc.GetModuleInfo(NULL, &game, true);
            HANDLE hPROCESS = OpenProcess(PROCESS_ALL_ACCESS, false, proc.getProcessID());
            DWORD ba = proc.FindSignature((DWORD)game.modBaseAddr, game.modBaseSize, (PBYTE)"\x89\x83\x80\x04\x00\x00\x8D\x55", "xxxxxxxx");//получаю адрес сигнатуры

            bool firstRun = true;
            bool qw = (DebugActiveProcess(proc.getProcessID()));//присоединяю отладчик к процессу Tutorial-i386.exe
            //proc.WriteMemory(ba, (PBYTE)a, 6);
            DEBUG_EVENT DebugEv = { 0 };
            if (SetPrivilege(SE_DEBUG_NAME, TRUE) == 0) {//здесь получаем привилегии дебаггера
                return DefWindowProc(hwnd, uMsg, wParam, lParam);
            }
            for (;;) {
                WaitForDebugEvent(&DebugEv, INFINITE);//здесь зависает и не откликается
            
                if (firstRun) {
                    HANDLE snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
                    if (snapShot == INVALID_HANDLE_VALUE) {
                        DebugActiveProcessStop(proc.getProcessID());
                        return DefWindowProc(hwnd, uMsg, wParam, lParam);
                    }
                    THREADENTRY32 te = { 0 };
                    te.dwSize = sizeof(THREADENTRY32);
                    while (Thread32Next(snapShot, &te)) {
                        if (te.th32OwnerProcessID != proc.getProcessID()) {
                            continue;
                        }
                        HANDLE hThread = OpenThread(THREAD_SET_CONTEXT | THREAD_GET_CONTEXT, 0, te.th32ThreadID);
                        CONTEXT ctx = { 0 };
                        ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_CONTROL;
                        GetThreadContext(hThread, &ctx);
                        ctx.Dr0 = ba;
                        ctx.Dr7 = MakeFlags(DebugRegister::kDR0, BreakState::kEnabledLocally, Condition::kWhenExecuted, Size::kByte);
                        SetThreadContext(hThread, &ctx);
                        break;
                    }
                    firstRun = false;
                }
                switch (DebugEv.dwDebugEventCode) {
                case EXCEPTION_DEBUG_EVENT: {
                    switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode) {
                    case EXCEPTION_SINGLE_STEP: {
                        if ((DWORD)DebugEv.u.Exception.ExceptionRecord.ExceptionAddress != ba) {
                            break;
                            HANDLE hThread = OpenThread(THREAD_SET_CONTEXT | THREAD_GET_CONTEXT, 0, DebugEv.dwThreadId);
                            CONTEXT ctx = { 0 };
                            ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_CONTROL;
                            GetThreadContext(hThread, &ctx);

                            ctx.Dr7 = 0;
                            SetThreadContext(hThread, &ctx);
                            break;
                        }
                    }
                    }
                }
                    ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, DBG_CONTINUE);
                }
                wsprintfW(buffer, L"Process base = 0x%X\nProcess size: 0x%X.\n Signature have been found at the 0x%X", (DWORD)game.modBaseAddr, game.modBaseSize, ba);
                proc.Close();
                InvalidateRect(hwnd, NULL, true);//
            }

 

В Google поискал, ничего нет. Xipho, ты вроде трейнер на c# делал. Не знаешь как на c++ значения регистров получить? Подскажи, пожалуйста, как брейкпоинт int 3 поставить на нужную инструкцию, чтобы WaitForDebugEvent его отлавливал как EXCEPTION_BREAKPOINT.

Изменено пользователем Desmos
Ссылка на комментарий
Поделиться на другие сайты

1 час назад, Desmos сказал:

В Google поискал, ничего нет

Мм, круть.

 

http://bfy.tw/JNfQ

 

А ещё можно с помощью ассемблерной вставки получать значение регистров.

А ещё можно с помощью dbghelp получать значения регистров.

 

CONTEXT lcContext;
lcContext.ContextFlags = CONTEXT_ALL;
GetThreadContext(m_cProcessInfo.hThread, &lcContext);

lcContext.Eax

 

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

9 часов назад, partoftheworlD сказал:

Мм, круть.

 

http://bfy.tw/JNfQ

 

 

У меня что в коде не так, как там? Я просто ненужное убрал в цикле и оставил EXCEPTION_DEBUG_EVENT. Я может не написал. Я имею ввиду, про вставку точки останова int 3 в нужное мне место в памяти, где лежит инструкция, ничего в Google нет. Я понимаю, что нужно использовать WriteProcessMemory(). Но мне нужен именно пример кода вставки через него. И еще, только через WaitForDebugEvent(),так как мне надо отладчик сделать. Я попросил объяснить, почему WaitForDebugEvent() ничего не возвращает, подчеркиваю, ничего, а просто замораживает поток трейнера на бесконечное время и все. Хотя я написал кнопку в программе, которая выполняла инструкцию.

Изменено пользователем Desmos
Ссылка на комментарий
Поделиться на другие сайты

4 часа назад, Desmos сказал:

Но мне нужен именно пример кода вставки через него.

Вставки опкода int3? Серьезно? WPM(handle, addr_inst, 0xcc, 1, 0);

 

4 часа назад, Desmos сказал:

почему WaitForDebugEvent() ничего не возвращает

Может потому что функция не отлавливает отладочное событие, если так отладчик в руки и дебаж свой отладчик, чтобы найти причину.

Return value

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

 

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

7 часов назад, partoftheworlD сказал:

Вставки опкода int3? Серьезно? WPM(handle, addr_inst, 0xcc, 1, 0);

 

Может потому что функция не отлавливает отладочное событие, если так отладчик в руки и дебаж свой отладчик, чтобы найти причину.

Return value

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

 

У меня всё заработало, только теперь на каждой итерации WaitForDebugEvent(...) возвращает Access violation. Причем не сразу а через секунду где-то.

Вот C++ код функции main:

Спойлер

int main() {
	ProcessManager proc;
	proc.GetProcessByName("FlashPlayer.exe");//получаем ID процесса
	std::cout << "pID: " << proc.getProcessID() << std::endl;
	system("pause");
    proc.Open();//открываем процесс с правами PROCESS_ALL_ACCESS
	
	MODULEENTRY32 game = { 0 };
	proc.GetModuleInfo(NULL, &game, true);//получаю начало модуля процесса для поиска сигнатуры
	DWORD ba = proc.FindSignature((DWORD)game.modBaseAddr, 0x76F20000, (PBYTE)"\xF3\x0F\x7E\x42\x20\xBA"/*\x00\x00\x00\x00\x81\xF2\x00\x00\x00\x00\x66\x0F\x57\xC9\xF2\x0F\x2A\xCA\xF2\x0F\x5C\xC1*/, "xxxxxx");//нахожу адресс сигнатуры
	std::cout << "Find signature?" << std::endl;
	printf("Game base addr  0x%X, game.modBaseSize0x%X \n", (DWORD)game.modBaseAddr, game.modBaseSize);
	printf("Address of signature 0x%X\n", ba);
	printf("Begin debug?\n");
	system("pause");
    bool firstRun = true;
	bool qw = (DebugActiveProcess(proc.getProcessID()));//присоединяю отладчик к процессу
	//proc.WriteMemory(ba, (PBYTE)a, 6);
	DEBUG_EVENT DebugEv = { 0 };
	if (SetPrivilege(SE_DEBUG_NAME, TRUE) == 0) {//устанавливаю привелегию отладчика
		return 0;
	}
	for (;;) {
		printf("Going to WaitForDebugEvent\n");
		WaitForDebugEvent(&DebugEv, INFINITE);//здесь ловятся отладочные события
		printf("The debug Event is catched! Code: %d exc code %d\n", DebugEv.dwDebugEventCode, DebugEv.u.Exception.ExceptionRecord.ExceptionCode);

        if (firstRun) {
			HANDLE snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
			if (snapShot == INVALID_HANDLE_VALUE) {
				DebugActiveProcessStop(proc.getProcessID());
				return 0;
			}
			THREADENTRY32 te = { 0 };
			te.dwSize = sizeof(THREADENTRY32);
			while (Thread32Next(snapShot, &te)) {
				if (te.th32OwnerProcessID != proc.getProcessID()) {
					continue;
				}
				HANDLE hThread = OpenThread(THREAD_SET_CONTEXT | THREAD_GET_CONTEXT, 0, te.th32ThreadID);
				CONTEXT ctx = { 0 };
				ctx.ContextFlags = CONTEXT_ALL;
				GetThreadContext(hThread, &ctx);
				ctx.Dr0 = ba;
				ctx.Dr7 = MakeFlags(DebugRegister::kDR0, BreakState::kEnabledLocally, Condition::kWhenExecuted, Size::kByte);
				SetThreadContext(hThread, &ctx);
				CloseHandle(hThread);
				break;
			}
			printf("Hook is wrote\n");
			firstRun = false;
		}
		if (DebugEv.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
			if (DebugEv.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP) {
				
				if (DebugEv.u.Exception.ExceptionRecord.ExceptionAddress != (PVOID)ba) {
					break;
				}
				printf("Exception is catched\n");
				printf("Exc The thread opens\n");
				HANDLE hThread = OpenThread(THREAD_SET_CONTEXT | THREAD_GET_CONTEXT, 0, DebugEv.dwThreadId);
				printf("Exc The thread is opened. Setting zero context\n");
				CONTEXT ctx = { 0 };
				printf("Exc The context is set 0. Setting Context Flags is CONTEXT_ALL\n");
				Sleep(50);
				ctx.ContextFlags = CONTEXT_ALL;
				printf("Exc The ContextFlags is CONTEXT_ALL. Getting Thread context\n");
				GetThreadContext(hThread, &ctx);
				printf("Exc The thread context is got. Getting the address Edx\n");
				printf("0x%X\n",ctx.Edx);
				printf("Exc The address have been got. Setting ctx.Dr7 flags with helping MakeFlags\n");
				ctx.Dr7 = 0;
				printf("Exc DR7 is flagged 0. SettingThreadContext\n");
				SetThreadContext(hThread, &ctx);
				printf("Going to the ContinueDebugEvent\n");
				CloseHandle(hThread);
			}
		}
      /////////////////////////////////////////////////////////////////здесь я поймал исключение
		if (DebugEv.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {//
			ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, DBG_CONTROL_BREAK);
			break;
		}
      //////////////////////////////////////////////////////////////////
		ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, DBG_CONTINUE);
		if (DebugEv.u.Exception.ExceptionRecord.ExceptionAddress == (PVOID)ba) {
			printf("The hook is writing again. Sleeping 10 ms\n");
			Sleep(50);
			printf("The thread opens\n");
			HANDLE hThread = OpenThread(THREAD_SET_CONTEXT | THREAD_GET_CONTEXT, 0, DebugEv.dwThreadId);
			printf("The thread is opened. Setting zero context\n");
			CONTEXT ctx = { 0 };
			printf("The context is set 0. Setting Context Flags is CONTEXT_ALL\n");
			ctx.ContextFlags = CONTEXT_ALL;
			printf("The ContextFlags is CONTEXT_ALL. Getting Thread context\n");
			GetThreadContext(hThread, &ctx);
			printf("The thread context is got. Setting DR0 is ba\n");
			ctx.Dr0 = ba;
			printf("DR0 is ba Setting ctx.Dr7 flags with helping MakeFlags\n");
			ctx.Dr7 = MakeFlags(DebugRegister::kDR0, BreakState::kEnabledLocally, Condition::kWhenExecuted, Size::kByte);
			printf("DR7 is flagged. SettingThreadContext\n");
			SetThreadContext(hThread, &ctx);
			printf("The Thread context is set to new context. Closing Handle\n");
			CloseHandle(hThread);
			printf("The handle is closed returning to beginning of the loop\n");
		}
		Sleep(700);
	}
	system("pause");
	return 0;
}

 

Отлавливаю условием:

if (DebugEv.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {//
	ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, DBG_CONTROL_BREAK);
	break;
}

Класс ProcessManager работает идеально, его работа рассчитана на открытие процесса и поиск сигнатуры.

Я вообще хочу сделать аналог функции Find out what adresses the instruction accesses в Cheat engine. То есть выводятся адреса, обращающиеся к инструкции, которую я искал.

Причем смотрите, сначала начинает работать нормально, а потом сыпятся Access Violation. Почему возникает обращение к не выделенной или удалённой области памяти, я не знаю? Что нужно сделать? Это проблема не с игрой точно, так как Cheat Engine выводит всё через Find out what adresses the instruction accesses нормальноПомогите, пожалуйста, я не хочу останавливаться на пол пути!

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

7 минут назад, Desmos сказал:

Find out what adresses the instruction accesses

Ну для этого нужны отладочные регистры DR0-DR3(вроде) и флаги в DR7(точно не помюню, но вроде так).

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

17 минут назад, Desmos сказал:

Почему возникает обращение к не выделенной или удалённой области памяти, я не знаю? Что нужно сделать?

 

Добавить проверку ограничивающую выход за пределы памяти и получения EXCEPTION_ACCESS_VIOLATION,

А отлавливать EXCEPTION_ACCESS_VIOLATION бесполезное занятие т.к. словив его уже ничего не сделать, ну если только rip/eip поменять на адрес инструкции до/после получения ошибки, но это не нормально.

if (DebugEv.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {//
	ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, DBG_CONTROL_BREAK);
	break;
}

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

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

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

 

Добавить проверку ограничивающую выход за пределы памяти и получения EXCEPTION_ACCESS_VIOLATION,

А отлавливать EXCEPTION_ACCESS_VIOLATION бесполезное занятие т.к. словив его уже ничего не сделать, ну если только rip/eip поменять на адрес инструкции до/после получения ошибки, но это не нормально.


if (DebugEv.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {//
	ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, DBG_CONTROL_BREAK);
	break;
}

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

Хорошо, попробую, только что мне даст вывод состояний регистров. Получается, что значение какого-то регистра изменилось во время отладки на то, которое ссылается на недопустимую область памяти? Я логгирование сделаю, завтра отпишу, может и сам соображу, исправлю и отпишу. Надеюсь, что причина в изменении какого-то регистра.

Изменено пользователем Desmos
Ссылка на комментарий
Поделиться на другие сайты

Вот крутая книга по написанию отладчика на Python, winapi и в африке winapi, так что, даже, если пишешь на плюсах проблем с портированием не будем, плюс узнаешь много нового.

https://www.reverse4you.org/translate/GHP/Book/Gray_Hat_Python.pdf

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

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

Вот крутая книга по написанию отладчика на Python, winapi и в африке winapi, так что, даже, если пишешь на плюсах проблем с портированием не будем, плюс узнаешь много нового.

https://www.reverse4you.org/translate/GHP/Book/Gray_Hat_Python.pdf

А там Memory Breakpoint есть? Типа брейкпоинт на память на доступ/запись?

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

3 часа назад, Senpai сказал:

А там Memory Breakpoint есть? Типа брейкпоинт на память на доступ/запись?

Ну да, если отладчик пишется, то он обязан поддерживать бряки памяти и хардварные, да и делать их легко, хардварный бряк через контекст и установка флага на dr7 регистр, бряк на память int3 и он кидает тебя в отладчик, и Guard page доступ/чтение/запись это установка EXCEPTION_GUARD_PAGE на страницу памяти, если она триггерится, то бряк срабатывает.

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

Вот, что-то писал когда-то 

Спойлер

case EXCEPTION_BREAKPOINT:
			{
				if (addres == (DWORD)debug_event.u.Exception.ExceptionRecord.ExceptionAddress)
				{
					m_thHandle = OpenThread(THREAD_ALL_ACCESS, false, debug_event.dwThreadId);
					CONTEXT lpContext;
					lpContext.ContextFlags = CONTEXT_FULL;
					GetThreadContext(m_thHandle, &lpContext);
					ccxt = lpContext;
					
		
					WriteProcessMemory(m_handle, (LPVOID)addres, &buffer, 1, NULL);
					FlushInstructionCache(m_handle, (void*)addres, 1);

					
					lpContext.EFlags |= 0x100; // EFLAGS_TF
					--lpContext.Eip; // правим на начало правильной инструкции
					SetThreadContext(m_thHandle, &lpContext);
					CloseHandle(m_thHandle);

					}
				}

 

Надеюсь, ещё не забросил

  • Понравилось 1
Ссылка на комментарий
Поделиться на другие сайты

Короче, проблема была при открытии контекста во время отлова события. А именно, присвоение поля contextflags: 

ctx.ContextFlags = CONTEXT_ALL;                                                 

Когда так делаешь, происходит краш игры, если присваивать только на получение из контекста только отладочных и целочисленных регистров, вылет игры не происходит. Как думаете, почему так? Я присвоил так:

ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_INTEGER     

И все заработало. То есть ошибка была именно там. Видимо это надо разбираться в механизме получения контекста во время отладочного события.

Я нашёл очень интересную книгу про это, если кому нибудь надо будет, я поделюсь:

Книга

Там хорошо расписано про флаги контекста, как нужно делать.

ЗЫ это не реклама, просто если у кого будет такая же проблема, прочитайте фрагмент книги по ссылке.

Изменено пользователем Desmos
  • Понравилось 1
  • Плюс 3
Ссылка на комментарий
Поделиться на другие сайты

53 минуты назад, Desmos сказал:

И все заработало. То есть ошибка была именно там. Видимо это надо разбираться в механизме получения контекста во время отладочного события.

Я нашёл очень интересную книгу про это, если кому нибудь надо будет, я поделюсь:

Книга

Вот так и должен выглядеть топик!!!!!!!!

 

А то очень раздражает: "Все я разобрался."         и усе.

 

  • Понравилось 1
Ссылка на комментарий
Поделиться на другие сайты

В 18.08.2018 в 21:29, Desmos сказал:

Короче, проблема была при открытии контекста во время отлова события. А именно, присвоение поля contextflags: 


ctx.ContextFlags = CONTEXT_ALL;                                                 

Когда так делаешь, происходит краш игры, если присваивать только на получение из контекста только отладочных и целочисленных регистров, вылет игры не происходит. Как думаете, почему так? Я присвоил так:


ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_INTEGER     

И все заработало. То есть ошибка была именно там. Видимо это надо разбираться в механизме получения контекста во время отладочного события.

Я нашёл очень интересную книгу про это, если кому нибудь надо будет, я поделюсь:

Книга

Там хорошо расписано про флаги контекста, как нужно делать.

ЗЫ это не реклама, просто если у кого будет такая же проблема, прочитайте фрагмент книги по ссылке.

плюсануть пока не могу почему-то, но будем считать, что плючанул

 

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

×
×
  • Создать...

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

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