Сразу скажу, что с асмом пока знаком плохо. Собираю тестовый драйвер под Win XP SP3 x86 на Intel. Хочу поставить хук на NtClose Хук делается так. Код (Text): __asm { pushad mov ecx, 0x176 rdmsr mov OldSyscall, eax mov eax, NewSyscall xor edx, edx wrmsr popad } Функция __declspec(naked) NewSyscall(). Внутри есть глобальная переменная _eax Код (Text): __asm { pushad pushfd push fs mov di, 0x30 mov fs, di mov eax, fs:[0x124] mov eax, [eax + 0x44] mov _eax, eax //push eax - так почему то не работало push edx call CollectProcess pop fs popfd popad jmp RetAddr } typedef struct Close_args { ULONG retaddr1; ULONG retaddr2; HANDLE hHandle; } *pClose_args; typedef NTSTATUS (*PNTClose)(HANDLE); __declspec(naked) CollectProcess(PVOID Stack) { switch(_eax) case 0x19: { pClose_args Args = Stack; if (...) { RetAddr = OldSyscall; break; } else { ... PNTClose RealNtClose = RealNtCloseAddress; // RealNtCloseAddress содержит корректный адресс функции NtClose RealNtClose(Args->hHandle); - BSOD ... RetAddr = Args->retaddr2; } break; } return; } При вызове собственно происходит падение в BSOD. Что собственно хочется получить - пользователь вызывает NtClose. Проверяются некоторые параметры, например UID пользователя. И если он подходящий, то происходт нормальный вызов. Если он не подходящий, то хочется вернуться к месту вызова вернув NT_ACCESS_DENIED. Помогите разобраться или дайте направление в котором копать. А то уже неделю не могу разобраться. Да и BSOD бывает разный. Или с кодом 0x000000d1 или 0x00000050. Ps часть кода написана по памяти, так что могут быть маленькие недочеты.
А на SDT хук не проще поставить? Это 0x000000d1 - DRIVER_IRQL_NOT_LESS_OR_EQUAL. Это 0x00000050 - PAGE_FAULT_IN_NONPAGED_AREA. В каком конкретно месте валится?
Валится в месте вызова прям. Пытался ходить отладчиком внутрь. Там происходит много различных вызовов и падает все далеко не сразу. Подозреваю, что может не сохраняю какие то регистры, но разобраться не могу. Собственно если возвращаться по OldSyscall то все нормально работает. Раньше и был хук SDT, теперь в ТЗ стоит отойти от него по ряду причин.
Насколько я понимаю - нет. Падение в BSOD происходит даже если закоментить скрытый под ... код. То есть попадаю в CollectProcess(PVOID Stack). Вызываю функцию NtClose по известному адрессу и происходит падение. Явно KeRaiselrql не вызывается 100%. В NtClose IRQL не повышается как я понимаю.
Neraverin А вы переключаете стек user-mode на стек kernel-mode? Вы уверены? В обработчике IRP_MJ_CLOSE драйвер может повысить IRQL, а стек user-mode - подкачиваемый, что может приводить к ошибкам.
Конечно падает, во первых !IF, это эквивалентно высочайшему IRQL, стек не переключен с DPC на локальный, задача не сохранена и пр. Код (Text): G_InitialStack ULONG ? ; KTHREAD.InitialStack G_PreviousMode ULONG ? ; KTHREAD.PreviousMode G_DebugActive ULONG ? ; KTHREAD.PreviousMode G_TrapFrame ULONG ? ; KTHREAD.TrapFrame G_KernelDr ULONG ? ; KPRCB.ProcessorState.SpecialRegisters Drx: test byte ptr [ebp + TsSegCs],MODE_MASK jz MarkFrame ; KM mov ebx,dr0 mov ecx,dr1 mov edi,dr2 mov [ebp + TsDr0],ebx mov [ebp + TsDr1],ecx mov [ebp + TsDr2],edi mov ebx,dr3 mov ecx,dr6 mov edi,dr7 mov [ebp + TsDr3],ebx mov [ebp + TsDr6],ecx xor ebx,ebx mov [ebp + TsDr7],edi mov dr7,ebx push edx mov edi,dword ptr fs:[PcPrcb] mov ecx,G_KernelDr mov ebx,[edi + ecx + SrKernelDr0] mov edx,[edi + ecx + SrKernelDr1] mov dr0,ebx mov dr1,edx mov ebx,[edi + ecx + SrKernelDr2] mov edx,[edi + ecx + SrKernelDr3] mov dr2,ebx mov dr3,edx mov ebx,[edi + ecx + SrKernelDr6] mov edx,[edi + ecx + SrKernelDr7] mov dr6,ebx mov dr7,edx jmp MarkFrame v86: add esp,KTRAP_FRAME_LENGTH ; V86 Es, Ds, Fs, Gs push 0 push 0 push 0 push 0 ; IRET frame. push KGDT_R3_DATA OR RPL_MASK ; Ss push 0 ; Esp push EFLAGS_INTERRUPT_MASK + EFLAGS_V86_MASK + 2H push KGDT_R3_CODE OR RPL_MASK ; Cs push 0 ; Eip ; #UD push edx mov ecx,PCR[PcGdt] mov edx,dword ptr ptr [ecx + 2] mov ecx,dword ptr [ecx + 4] and ecx,0FF000000H and edx,000FFFFFFH or ecx,edx pop edx jmp ecx ; KiTrap06 ; o !IF, !VM ; xKiFastCallEntry proc C push KGDT_R0_PCR mov ecx,KGDT_R3_DATA OR RPL_MASK pop fs mov ds,ecx push 2 mov es,ecx popfd ; !DF etc. ; Переключаем DPC стек. mov ecx,PCR[PcTss] mov esp,[ecx + TssEsp0] push KGDT_R3_DATA OR RPL_MASK ; Ss push edx ; Esp add edx,2*4 ; Arg's or byte ptr [esp + 1],(EFLAGS_INTERRUPT_MASK shr 8) ; IF push KGDT_R3_CODE OR RPL_MASK ; Cs push dword ptr ds:[USER_SHARED_DATA + UsSystemCallReturn] ; Eip push 0 push ebp push ebx push esi push edi mov ebx,PCR[PcSelfPcr] push KGDT_R3_TEB OR RPL_MASK ; Fs mov esi,dword ptr [ebx + PcPrcbData + PbCurrentThread] push dword ptr [ebx + PcExceptionList] mov dword ptr [ebx + PcExceptionList],EXCEPTION_CHAIN_END mov ecx,G_InitialStack mov ebp,dword ptr [esi + ecx] push MODE_MASK ; UM sub esp,TsPreviousPreviousMode mov ecx,G_PreviousMode sub ebp,NPX_FRAME_LENGTH + KTRAP_FRAME_LENGTH mov byte ptr [esi + ecx],MODE_MASK ; V86 cmp ebp,esp mov ecx,Dr7 jne v86 mov dword ptr [ebp + TsDr7],0 test ecx,ecx mov [ebp + TsDbgArgPointer],edx mov [ebp + TsDbgArgMark],0BADB0D00H jnz Drx ; Normal SET_DEBUG_DATA. mov ebx,[ebp + TsEbp] mov edi,[ebp + TsEip] mov ecx,G_TrapFrame mov [ebp + TsDbgEbp],ebx mov [ebp + TsDbgEip],edi mov dword ptr [esi + ecx],ebp MarkFrame: sti ; IF Необходимые константы в kimacro.inc Плохой способ, лучше распарсить саму KiFastCallEntry() и скопировать код в буфер. При этом нужно сохранить в стеке ветвления и поправить их. При этом нужен всего дизасм длин. Но я бы не стал это юзать. Нужно определять смещение некоторых полей. Более того это потенциальный способ убить систему, при вызове с TF ось упадёт, не зависимо от способа формирования трап фрейма.
Спасибо за помощь. Буду изучать сегодня в этом направлении. Вот только не совсем понял про IF. Если IА эквивалентно высочайшему IRQL, то падать это должно всегда. Однако если просто убрать вызов по указателю (оставив при этом остальную логику), то все нормально работает.
Neraverin Прерывания замаскированы все. IRQL работает только если прерывания размаскированы. Иначе быть не может, ведь камень их не пропускает. В принципе достаточно перезагрузить стек и селекторы для работы в KM. При этом задача не сохранена и текущий тред ядерный. Для этого достаточно перезагрузить стек и сегментные регистры. DPC стек это стек уникальный для процессора, но общий для всех потоков исполняемых на данном процессоре. Ловушки и прерывания используют локальный стек, который определён в TSS, туда он загружается шедулером. DPC стек загружается однократно в MSR при инициализации оси. В этом стеке и начинает исполняться сискал. Если разрешить прерывания, то при переключении задачи и последующем вызове сискала на текущем процессоре стек будет затёрт, изза этого восстановление состояния при следующем переключении задач невозможно, это приведёт к багчеку. Перехват сискалов перезаписью Ip в MSR это тупой способ. Попытка вызова сискала с взведённым TF приведёт к багчеку(ядро хэндлит это проверкой адреса фолта, но так как он будет изменён вохникнет фолт не обработанный что приведёт к багчеку). Одно нажатие F7 в олли на шлюзе и оси упадёт.