Итак, Call gate установлен и имеет один параметр. При входе в обработчик стек выглядит так: Ss Esp Param Cs Eip <- Current ESP Сам обработчик (смещения для _KTHREAD.TrapFrame и _KTHREAD.PreviousMode hardcoded для XP build 2600, не обращаем внимания): Код (Text): __asm { /// На входе в Call Gate прерывания разрешены, поэтому cli /// Формируем TRAP_FRAME и попутно настраиваем сегментные регистры push dword ptr [ esp + 0x10 ] /// _KTRAP_FRAME.SegSs push dword ptr [ esp + 0x10 ] /// _KTRAP_FRAME.Esp pushfd pop eax and eax, 0xFFFCFFFF /// Сбрасываем EFLAGS_VM и EFLAGS_RF or eax, 0x00000200 /// Устанавливаем EFLAGS_IF push eax /// _KTRAP_FRAME.Eflags push dword ptr [ esp + 0x10 ] /// _KTRAP_FRAME.SegCs push dword ptr [ esp + 0x10 ] /// _KTRAP_FRAME.Eip push 0 /// _KTRAP_FRAME.ErrCode push ebp /// _KTRAP_FRAME.ebp push ebx /// _KTRAP_FRAME.ebx push esi /// _KTRAP_FRAME.esi push edi /// _KTRAP_FRAME.edi push fs /// _KTRAP_FRAME.SegFs /// Настраиваем fs mov bx, 0x30 /// GDT_SELECTOR_R0_PCR mov fs, bx sub esp, 0x14 /// не заполняем _KTRAP_FRAME.ExceptionList (пока), .PreviousPreviousMode, .eax, .ecx, .edx push ds /// _KTRAP_FRAME.SegDs push es /// _KTRAP_FRAME.SegEs push gs /// _KTRAP_FRAME.SegGs /// Настраиваем ds, es mov bx, 0x23 /// KGDT_R3_DATA or RPL_MASK mov ds, bx mov es, bx sub esp, 0x30 /// выделяем остаток памяти под frame mov ebp, esp /// ebp <- Trap Frame /// Сбрасываем все ненужные флаги push 2 popfd /// Сбрасываем Dr7, отладочные регистры в Trap Frame не сохраняем xor eax, eax mov Dr7, eax /// Exception list push dword ptr fs:[0] mov dword ptr fs:[0], 0xFFFFFFFF pop dword ptr [ebp + 0x4C] /// Устанавливаем _KTRAP_FRAME в _KTHREAD mov ebx, dword ptr fs:[0x124] mov dword ptr [ebx + 0x134] ,ebp /// Устанавливаем PreviousMode в _KTHREAD mov byte ptr [ebx + 0x140], 1 /// Разрешаем прерывания sti } To Do __asm { cli lea esp,[ebp + 0x30] pop gs pop es pop ds add esp, 0x14 pop fs pop edi pop esi pop ebx pop ebp add esp, 0x18 sti retf 0x4 } При выходе из колгейта приложение валится с PF Код (Text): PAGE_FAULT_IN_NONPAGED_AREA (50) Invalid system memory was referenced. This cannot be protected by try-except, it must be protected by a Probe. Typically the address is just plain bad or it is pointing at freed memory. Arguments: Arg1: f420f00c, memory referenced. Arg2: 00000000, value 0 = read operation, 1 = write operation. Arg3: 804fc83a, If non-zero, the instruction address which referenced the bad memory address. Arg4: 00000000, (reserved) FAULTING_IP: nt!KeContextFromKframes+212 804fc83a 8b8798020000 mov eax,dword ptr [edi+298h] Причём в колгейтах без параметров всё работает отлично. Чувсвтую, что упустил что-то, но что пока не могу сообразить.
Немного переделал. Теперь выход из Call Gate организовал с помощью nt!Kei386EoiHelper. Но. Где-то один раз из 1000 на входе или выходе из колгейта наступает прерывание hal!Halp8254ClockInterrupt, всё валится, причём: Код (Text): IRQL_NOT_LESS_OR_EQUAL (a) An attempt was made to access a pageable (or completely invalid) address at an interrupt request level (IRQL) that is too high. This is usually caused by drivers using improper addresses. If a kernel debugger is available get the stack backtrace. Arguments: Arg1: 000004cd, memory referenced Arg2: 000000ff, IRQL Arg3: 00000001, bitfield : bit 0 : value 0 = read operation, 1 = write operation bit 3 : value 0 = not an execute operation, 1 = execute operation (only on chips which support this level of status) Arg4: 8281bd1d, address which referenced memory Код (Text): WRITE_ADDRESS: 000004cd CURRENT_IRQL: 2 FAULTING_IP: hal!Halp8254ClockInterrupt+79 8281bd1d fe4711 inc byte ptr [edi+11h] Код (Text): hal!Halp8254ClockInterrupt: 8281bca4 54 push esp 8281bca5 55 push ebp 8281bca6 53 push ebx 8281bca7 56 push esi 8281bca8 57 push edi 8281bca9 83ec54 sub esp,54h 8281bcac 8bec mov ebp,esp 8281bcae 894544 mov dword ptr [ebp+44h],eax 8281bcb1 894d40 mov dword ptr [ebp+40h],ecx 8281bcb4 89553c mov dword ptr [ebp+3Ch],edx 8281bcb7 f7457000000200 test dword ptr [ebp+70h],20000h 8281bcbe 75bc jne hal!V86_Hci_a (8281bc7c) 8281bcc0 66837d6c08 cmp word ptr [ebp+6Ch],8 8281bcc5 741f je hal!Halp8254ClockInterrupt+0x42 (8281bce6) 8281bcc7 8c6550 mov word ptr [ebp+50h],fs 8281bcca 8c5d38 mov word ptr [ebp+38h],ds 8281bccd 8c4534 mov word ptr [ebp+34h],es 8281bcd0 8c6d30 mov word ptr [ebp+30h],gs 8281bcd3 bb30000000 mov ebx,30h 8281bcd8 b823000000 mov eax,23h 8281bcdd 668ee3 mov fs,bx 8281bce0 668ed8 mov ds,ax 8281bce3 668ec0 mov es,ax 8281bce6 648b1d00000000 mov ebx,dword ptr fs:[0] 8281bced 64c70500000000ffffffff mov dword ptr fs:[0],0FFFFFFFFh 8281bcf8 895d4c mov dword ptr [ebp+4Ch],ebx 8281bcfb 81fc00000100 cmp esp,10000h 8281bd01 0f8249ffffff jb hal!Abios_Hci_a (8281bc50) 8281bd07 c7456400000000 mov dword ptr [ebp+64h],0 8281bd0e 648b0d24010000 mov ecx,dword ptr fs:[124h] 8281bd15 fc cld 8281bd16 648b3d20000000 mov edi,dword ptr fs:[20h] 8281bd1d fe4711 inc byte ptr [edi+11h] 8281bd20 807f1101 cmp byte ptr [edi+11h],1 8281bd24 753b jne hal!Halp8254ClockInterrupt+0xbd (8281bd61) 8281bd26 0f31 rdtsc ... Код (Text): Trap Frame eip=8281bd1d esp=927dbd2c ebp=927dbd2c iopl=0 nv up di ng nz na po nc cs=0008 ss=0010 ds=0023 es=0023 fs=003b gs=0000 efl=00000082 hal!Halp8254ClockInterrupt+0x79: 8281bd1d fe4711 inc byte ptr [edi+11h] ds:0023:000004cd=?? fs=003b !!!! как такое может быть ?
SadKo, на входе куда? Вполне вероятно, что в hal!Halp8254ClockInterrupt мы попадаем с fs = 3b. Но она, как и любой другой обработчик прерывания, явно перегружает fs в 30. bp на hal!Halp8254ClockInterrupt ставить бесполезно, т.к. она вызывается каждые 10 мс или что-то около того. bp /t windbg не съел. Да и вообще у виндбг есть проблемы при отладке операций с сегментными регистрами.
Поковырял ещё немного. Если использовать тот же пролог для interrupt gate, или при обработке sysenter (c минимальными изменениями конечно), то всё работает как часы. Такое впечатление, что прерывание hal!Halp8254ClockInterrupt срабатывает ДО первой инструкции пролога cli (!!!!!). А ведь на самом деле: Int и SysEnter сбрасывают IF, а far call - нет.
Сегодня провёл маленький эксперимент. UserMode: Код (Text): start: call fword ptr [ FarCall ] ; MyDriver!DispatchRoutine jmp start KernelMode: Код (Text): MyDriver!DispatchRoutine: 95a0c030 cb retf Как и думал - код валится по той же причине. Получается, что механизм Call Gate'ов нельзя использовать в винде!?