Захотел я протрассировать пошагово вызов API начиная с юзермода с переходом через колгейт и вплоть до самого низа. Для этого загрузил драйвер. Моё юзермодное приложение через DeviceIoControl вызывает его функцию, которая прописывает в IDT свой обработчик INT1, устанавливает флаг трассировки и возвращает управление. По идее мы должны были так пошагово вернуться в нашу юзермодную функцию. Но только по идее. Вызов IoCompleteRequest приводит к access violation-у. Access violation глупый - IoCompleteRequest вызывает KeInitializeApc примерно таким образом: KeInitializeApc(&pIrp->Tail.Apc,pIrp->Tail.Overlay.Thread,pIrp->ApcEnvironment,IopCompleteRequest,IopAbortRequest,0,0,0); при этом при трассировке &pIrp->Tail.Apc оказывается равным нулю, в результате чего далее по коду получаем mov word ptr [eax],12h ds:0023:00000000=???? со всеми вытекающими. Конечно, без трассировки всё работает. 1. Есть у кого соображения каким образом установленный флаг TF мешает нормальному исполнению программы? (трассируется чистое апи винды, никаких специальных противоотладочных трюков, протекторов и пр.) 2. Возможно ли вообще осуществить такой трейс? Вот код моего обработчика: void __declspec( naked noreturn ) int01_handler() { __asm { push [esp] call next_step iretd }; } void next_step( VOID* next_eip ) { if ( ( KeGetCurrentIrql() == PASSIVE_LEVEL ) && ( ((USHORT*)next_eip)[0] == 0x350f ) ) { DbgPrint("sysexit at %08lx", next_eip); } }
может так Код (Text): void __declspec( naked noreturn ) int01_handler() { __asm { push [esp] call next_step or [esp], 0x10000 iretd }; }
Я так понял, имелся в виду флаг RF. Тогда должно быть не or [esp], 0x10000, а or [esp+8], 0x10000. По [esp] находится старый eip, по [esp+6] - cs, а уже по [esp+8] - EFLAGS. В нём я и попробовал только-что установить бит RF, как ты посоветовал и как пишут в интелевском талмуде. Результат точно такой же - багчек в том же месте, что и в первом моём посте. Нужно сказать, что и раньше отладка на вид происходила нормально (обработчик вызывается для вполне внятных и правдоподобных eip-ов, вроде нормально возвращается из него), а потом вылетало с багчеком. Похоже, что установка TF таки вносит какие-то изменения. В любом случае, большое спасибо за мысли. Может, есть ещё какие-то соображения на счёт этой проблемы?
да, естественно прошу прощения за описку только тогда [esp + 6], а не [esp + 8] (CS находится по [esp + 4]) не думаю а что если сделать pushad Код (Text): __asm { pushad push [esp] call next_step or [esp + 6], 0x10000 popad iretd }; да, кстати next_step() в конце делает ret 4?
Флаг RF ничего не дал. Насколько я понял, он нужен на тот случай, когда одна и та же комманда попадает под отладку по нескольким условиям, скажем когда комманда int 1 исполняется с установленным TF. Конвенция была действительно stdcall по дефолту. Проблема была с регистрами - я просто не учёл , что компилер не сохраняет значение eax в прологе, даже если функция объявлена как возвращающая void. И не только eax, кстати. Так что за подсказку, мужики, спасибо большущее. Теперь следующая проблема - конструкция та же, INT 1 перехвачен, TF установлен НО EIP уже в юзермоде, то есть уже изменяется уровень привилегий. Что получается - попадаем в обработчик, корректно проходим обработчик, но, очевидно, после IRETD имеем Код (Text): *** Fatal System Error: 0x0000007f (0x0000000D,0x00000000,0x00000000,0x00000000) причём WinDbg при анализе показывает, что Код (Text): FAULTING_SOURCE_CODE: 81: void next_step( VOID* next_eip ) 82: { 83: LARGE_INTEGER sleep_time; 84: > 85: DbgPrint("[hillo] step at %08lx\n", next_eip); 86: 87: sleep_time.QuadPart = (LONGLONG)SECONDS(0.1); 88: KeDelayExecutionThread( KernelMode, FALSE, &sleep_time ); что не есть правдой, т.к эти комманды были успешно пройдены - я расставил по всей функции int 3. Пошагово пройти функцию я, разумеется, не могу, т.к. сам же перехватил int 1. Код (Text): void next_step( VOID* next_eip ) { LARGE_INTEGER sleep_time; DbgPrint("[hillo] step at %08lx\n", next_eip); sleep_time.QuadPart = (LONGLONG)SECONDS(0.1); KeDelayExecutionThread( KernelMode, FALSE, &sleep_time ); if ( (ULONG)next_eip == 0x8053c91d ) { DbgPrint("[hillo] break at popfd\n", next_eip); __asm int 3; } } void __declspec( naked noreturn ) int01_handler() { __asm { push ebp mov ebp, esp push eax push ebx push ecx push edx push esi push edi push [ebp+4] // EIP call next_step pop edi pop esi pop edx pop ecx pop ebx pop eax pop ebp iretd }; } Догадываюсь, что не сделал чего-то перед iretd-ом, но что... Гугл ничего не дал, дизасм нтоскернелевского _KiTrap01 тоже пока ничего. Может у вас будут какие-то мысли по этому поводу?