Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #83, НОЯБРЬ 2005 г.

Строим отладчик

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 сохранить оригинальный байт с точки входа, так как нам понадобится его восстановить при срабатывании брекпоинта.

Назад на стр. 083-112-5  Содержание  Вперед на стр. 083-112-7