Строим отладчик Ms-Rem (Ms-Rem@yandex.ru) Хакер, номер #083, стр. 083-112-6 Для того чтобы отлаживать какой-либо процесс, его надо сначала запустить специальным образом. Делается это с помощью CreateProcess с установленными в dwCreationFlags флагами DEBUG_PROCESS и DEBUG_ONLY_THIS_PROCESS. Первый будет означать, что процесс запускается в режиме отладки, а второй - что отладка не распространяется на все запускаемые им дочерние процессы. Процесс запустится уже в остановленном состоянии, сгенерируется первое отладочное событие, несущее информацию о срабатывании брекпоинта, и процесс не будет выполняться до тех пор, пока это событие не будет обработано отладчиком. Для получения отладочного события дебаггер должен вызвать функцию WaitForDebugEvent, которая ждет наступления события и сохраняет информацию о нем в структуре TDebugEvent. Для продолжения выполнения программы отладчику нужно вызвать ContinueDebugEvent. После запуска процесса мы сразу же получим событие, описывающее сработавший брекпоинт, но он будет не на точке входа в программу, а в функции BaseProcessStart из kernel32.dll, с которой начинается выполнение программы. Нам нужно установить «бряк» на точке входа. Для того чтобы определить, где она находится придется загрузить исполнимый файл программы и прочитать интересующий нас адрес из PE-заголовка. Для этого можно использовать функцию LoadLibraryEx: pImage := pointer(LoadLibraryEx(PChar(DbgApp), 0, DONT_RESOLVE_DLL_REFERENCES)); ntHeaders := pointer(dword(pImage) + pImage^._lfanew); EntryPoint := pointer(ntHeaders^.OptionalHeader.ImageBase + ntHeaders^.OptionalHeader.AddressOfEntryPoint); FreeLibrary(dword(pImage)); Флаг DONT_RESOLVE_DLL_REFERENCES означает, что нам нужно просто загрузить PE-файл без настройки его импортов и исполнения точки входа. Без указания этого флага файл грузиться просто не будет. В случае успеха функция LoadLibraryEx возвращает параметр hmodule, который на самом деле является ни чем иным, как указателем на MZ-заголовок файла. Прибавив к нему значение поля e_lfanew этого заголовка, мы получим указатель на ImageNtHeaders, откуда можно извлечь базовый адрес загрузки PE-файла (ImageBase) и RVA его точки входа (AddressOfEntryPoint). Сумма этих чисел и будет адресом точки входа в загруженной программе. После получения точки входа файл можно выгрузить с помощью FreeLibrary, так как он больше не понадобится. Теперь мы можем начать обработку отладочных событий. В первом из них установим брекпоинт, записав с помощью WriteProcessMemory байт CC (опкод команды int 3) на точку входа программы. while true do begin WaitForDebugEvent(DbgEvent, INFINITE); if DbgEvent.dwDebugEventCode = EXCEPTION_DEBUG_EVENT then case DbgEvent.Exception.ExceptionRecord.ExceptionCode of EXCEPTION_BREAKPOINT : begin Inc(BreakNum); if BreakNum = 1 then begin ReadProcessMemory(Pr.hProcess, EntryPoint, @OldByte, 1, Bytes); NewByte := $CC; WriteProcessMemory(Pr.hProcess, EntryPoint, @NewByte, 1, Bytes); end Перед этим нужно с помощью ReadProcessMemory сохранить оригинальный байт с точки входа, так как нам понадобится его восстановить при срабатывании брекпоинта. |