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: Станислав Будинов

содержание | назад | вперед