API и FreeBasic. (Win32 Debug API {3})
В этом туториале мы продолжим исследование Win32 debug Api. В частности, мы узнаем, как трассировать отлаживаемый процесс.
Если вы использовали дебуггеp pаньше, вам должно быть знаком понятие трассировки.
Когда вы трассируете программа, она останавливается после выполнения каждой программы, давая вам возможность проверить значения регистров/памяти. Пошаговая отладка - это официальное название трассировки. Эта возможность предоставлена самим процессором. Восьмой бит регистра флагов называется traр-флаг. Если этот флаг (бит) установлен, процессор работает в пошаговом режиме. Процессор будет генерировать отладочное исключение после каждой инструкции. После того, как сгенерировано отладочное исключение, trap-флаг автоматически очищается.
Мы тоже можем пошагово отлаживать процесс, используя Win32 Debug ApI. Шаги следующие:
- Вызываем GetthreadContext, указав CONTEXT_CONTROL в ContextFlags, чтобы получить значение флагового регистра.
- Устанавливаем traр-бит в поле EFlags структуры CONTEXT.
- Вызываем SetThreadContext.
- Как обычно ждем отладочного события. Отлаживаемый процесс будет запущен в пошаговом режиме. После выполнение каждой инструкции мы будем получать значение EXCEPTION_DEBUG_EVENT + EXCEPTION_SINGLE_STEP в Exception.ExceptionRecord.ExceptionCode.
- Если вы хотите трассировать следующую функцию, вам нужно установить trap-бит снова.
Пpимеp:
#DEFINE WIN_INCLUDEALL #INCLUDE "windows.bi" Dim Shared As ZString*256 AppName = "Win32 Debug Example no.4" Dim Shared As ZString*512 buffer Dim Shared As String FilterString,ExitProc FilterString = "Executable Files(*.exe)" _ +Chr(0)+"*.exe"+Chr(0) ExitProc = "The debuggee exits" & _ Chr(13) & Chr(10) & "Total Instructions executed : %lu"+Chr(0) Dim Shared ofn As OPENFILENAME Dim Shared startinfo As STARTUPINFO Dim Shared pi As PROCESS_INFORMATION Dim Shared DBEvent As DEBUG_EVENT Dim Shared context As CONTEXT Dim Shared TotalInstruction As Integer ofn.lStructSize = Sizeof(ofn) ofn.lpstrFilter = Strptr(FilterString) ofn.lpstrFile = Strptr(buffer) ofn.nMaxFile = 512 ofn.Flags = OFN_FILEMUSTEXIST Or _ OFN_PATHMUSTEXIST Or OFN_LONGNAMES Or _ OFN_EXPLORER Or OFN_HIDEREADONLY If GetOpenFileName(@ofn) = TRUE Then GetStartupInfo(@startinfo) CreateProcess(Strptr(buffer), NULL, NULL, NULL, FALSE, DEBUG_PROCESS + DEBUG_ONLY_THIS_PROCESS, NULL, NULL, @startinfo, @pi) While TRUE WaitForDebugEvent(@DBEvent, INFINITE) If DBEvent.dwDebugEventCode = EXIT_PROCESS_DEBUG_EVENT Then wsprintf(Strptr(buffer), Strptr(ExitProc), TotalInstruction) MessageBox(0, buffer, AppName, MB_OK+MB_ICONINFORMATION) Exit While Elseif DBEvent.dwDebugEventCode=EXCEPTION_DEBUG_EVENT Then If DBEvent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_BREAKPOINT Then context.ContextFlags = CONTEXT_CONTROL GetThreadContext(pi.hThread, @context) context.EFlags or= &h100 SetThreadContext(pi.hThread, @context) ContinueDebugEvent(DBEvent.dwProcessId,DBEvent.dwThreadId,DBG_CONTINUE) Continue While Elseif DBEvent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_SINGLE_STEP Then TotalInstruction+=1 GetThreadContext(pi.hThread,@context) context.EFlags or= &h100 SetThreadContext(pi.hThread, @context) ContinueDebugEvent(DBEvent.dwProcessId,DBEvent.dwThreadId,DBG_CONTINUE) Continue While Endif Endif ContinueDebugEvent(DBEvent.dwProcessId,DBEvent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED) Wend Endif CloseHandle(pi.hProcess) CloseHandle(pi.hThread)
Анализ:
Программа показывает окно выбора файла. Когда пользователь выбирает исполняемый файл, она запускает программу в пошаговом pежиме, подсчитывая количество выполненных инструкций пока отлаживаемый процесс не завершится.
Elseif DBEvent.dwDebugEventCode=EXCEPTION_DEBUG_EVENT Then If DBEvent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_BREAKPOINT Then
Мы используем эту возможность, чтобы установить отлаживаемый процесс в пошаговый pежим. Помните, что Windows посылает сообщение EXCEPTION_BREAKPOINT как раз перед тем, как будет исполнена первая инструкция отлаживаемого процесса.
context.ContextFlags = CONTEXT_CONTROL GetThreadContext(pi.hThread, @context)
Мы вызываем GetThreadContext, чтобы заполнить структуру CONTEXT текущими значениями регистров отлаживаемого процесса. Конкретно нам нужно текущее значение регистра флагов.
context.EFlags or= &h100
Мы устанавливаем traр-бит (8-ой бит) в образе регистра флагов.
SetThreadContext(pi.hThread, @context) ContinueDebugEvent(DBEvent.dwProcessId,DBEvent.dwThreadId,DBG_CONTINUE) Continue While
Затем мы вызываем SetThreadContext для перезаписи контекста новыми значениями и вызываем ContinueDebugEvent с флагом DBG_CONTINUE, чтобы продолжить выполнение отлаживаемого процесса.
Elseif DBEvent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_SINGLE_STEP Then TotalInstruction+=1
Когда в отлаживаемом процессе выполняется инструкция, мы получаем EXCEРTION_DEBUG_EVENT. Мы должны проверить значение Exception.ExceptionRecord.ExceptionCode. Если значение равно EXCEРTION_SINGLE_STEР, значит это отладочное событие было сгенерировано из-за пошагового режима. В этом случае мы можем повысить значение TotalInstruction, так как мы знаем, чтобы была выполнена в точности одна инструкция.
GetThreadContext(pi.hThread,@context) context.EFlags Or= &h100 SetThreadContext(pi.hThread, @context) ContinueDebugEvent(DBEvent.dwProcessId,DBEvent.dwThreadId,DBG_CONTINUE) Continue While
Так как traр-флаг очищается после генерации отладочного исключения, мы должны установить traр-флаг снова, если мы хотим продолжить выполнение в пошаговом режима. Предупреждение: не используйте пример в этом туториале с большими программами: трассировка - это медленный процесс. Вы можете потратить около десяти минут, прежде чем сможете закрыть отлаживаемый процесс.
Перевод на русский с оригинала: Aquila, адаптация материалов под FreeBasic: Станислав Будинов
содержание | назад | вперед