В обработчике калбэка ловлю ntdll.dll нахожу таблицу экспорта и пытаюсь изменить атрибуты защиты на EAT для функции LdrLoadDll NtProtectVirtualMemory возвращает C0000005 - STATUS_ACCESS_VIOLATION Прочитал ветку похожую http://wasm.ru/forum/viewtopic.php?id=25591&p=1 Ну и еще есть некоторые ответы по той теме, например автор выяснил что это действительно так, потому как внутрях NtProtectVirtualMemory Код (Text): result = ObReferenceObjectByHandle(ProcessHandle, 8u, PsProcessType, AccessMode[0], &BugCheckParameter1, 0); if ( result >= 0 ) { if ( v15 != BugCheckParameter1 ) { KeStackAttachProcess((ULONG_PTR)BugCheckParameter1, (int)&v12); v17 = 1; } v13 = MiProtectVirtualMemory(BugCheckParameter1, &v18, &v19, NewProtect, &v14); if ( v17 ) KeUnstackDetachProcess(&v12); ObfDereferenceObject(BugCheckParameter1); Кроме того, на сколько я понимаю, в это колбэке мы и так приаттачены к процессу от которого идет нотифи. исходя из этого у меня в коде макрос NtCurrentProcess() Код (Text): NtProtectVirtualMemory(NtCurrentProcess(), &BaseAddress, &Size, PAGE_READWRITE, &ulProtection); Ну и последнее, т.к я вызываю кернел версию NtProtectVirtualMemory, то скорее всего у меня они НЕ должны быть в юзер АП Вобщем мистика для меня...
KTHREAD.PreviousMode каков? Usermode, Kernelmode? Если один вызывает NtProtectVirtualMemory() с PreviousMode==Usermode и параметры-указатели определяют адреса за границей пользовательского пространства, то функция бросает исключение, ловит его – и выдаёт статус AV. Ведь если бы указатели не проверялись, пользовательский поток мог бы записать информацию по произвольному адресу в ядро, просто вызвав NtProtectVirutalMemory(), разве нет? Корректный путь – вызвать ZwProtectVirtualMemory().
Да, в нотифи PreviousMode = Usermode ZwProtectVirtualMemory вызывать не очень хочется ИМХО это некрасиво в коде, да и под фильтры чужие можем попасть. Нашел кусок кода Код (Text): PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { // // Capture the region size and base address under an exception handler. // try { ProbeForWritePointer (BaseAddress); ProbeForWriteUlong_ptr (RegionSize); ProbeForWriteUlong (OldProtect); // // Capture the region size and base address. // CapturedBase = *BaseAddress; CapturedRegionSize = *RegionSize; } except (EXCEPTION_EXECUTE_HANDLER) { // // If an exception occurs during the probe or capture // of the initial values, then handle the exception and // return the exception code as the status value. // return GetExceptionCode(); } Не очень понятно из-за чего ловится exception, мб можно на время вызова переставить PreviousMode в KernelMode, не очень охото искать MiProtectVirtualMemory вызывая на прямую
>под фильтры чужие можем попасть Но в таком случае фильтрование может осуществляться и на уровне NtProtectVirtualMemory(), и на уровне MiProtectVirtualMemory(). Т.е. мы бы не стали заморачиваться по этому поводу. >Не очень понятно из-за чего ловится exception Функции "ProbeForRead*" убеждаются, что параметр лежит в пользовательской области. Функции "ProbeForWrite*" убеждаются, что параметр лежит в пользовательской области и пытаются записать что-либо в каждую страницу, занимаемую параметром. +Проверка выравнивания. В случае несоответствия выполняется бросок. >мб можно на время вызова переставить PreviousMode в KernelMode Ну а кто ж запретит
freyr Я не нашел вопроса. Что тебе нужно, что подсказывать то? Там ясно написано как использовать ZwProtectVirtualMemory. У клерка научился?)) Забудь нафиг про неэкспортируемые внутренние апи. Единственный _корректный_ путь изменения аттрибутов защиты юзермодных страниц, особенно проекций секций, это ZwProtectVirtualMemory. Иначе у тебя есть все шансы похерить системный кеш, что, разумеется, ничем хорошим не закончится.
Sol_Ksacap Спасибо большое, за объяснение всех моментов ! Надеюсь прочитать раньше чем "они" заменят индекс. (замена начальных инструкций функции не в счет, уже никто давно так не балуется в ядре ) И тем более так Код (Text): mov eax, large fs:124h mov [eax+140h], 0 Так ?
Great Эм.. Код (Text): ENTRY_MAXIMUM_LENGTH equ 150H OPCODE_CALL equ 0E8h OPCODE_RET equ 0C3h QueryMiProtectVirtualMemory proc uses ebx esi edi NtImageBase:PVOID, Sst:PVOID, MiProtectVirtualMemory:PVOID Local ImageHeader:PIMAGE_NT_HEADERS Local LocalSst:PVOID Local pNtProtectVirtualMemory:PVOID Local SizeOfOpcode:ULONG Local LastIp:PVOID ENTER_SEH invoke ImageNtHeader, NtImageBase, addr ImageHeader test eax,eax jnz exit_ invoke QueryNtProtectVirtualMemory, NtImageBase, addr LocalSst, addr pNtProtectVirtualMemory test eax,eax mov ebx,pNtProtectVirtualMemory jnz exit_ lea esi,[ebx + ENTRY_MAXIMUM_LENGTH] step_: invoke QueryInstructionSize, ebx, addr SizeOfOpcode test eax,eax jnz exit_ add ebx,SizeOfOpcode cmp word ptr [ebx],75FFH ; push dword ptr ss:[ebp + 14], (push NewProtectWin32) jne next_ cmp byte ptr [ebx + 2],14H jne next_ mov edi,6 mov LastIp,ebx @@: invoke QueryInstructionSize, ebx, addr SizeOfOpcode test eax,eax jnz exit_ add ebx,SizeOfOpcode dec edi jnz @b cmp byte ptr [ebx],OPCODE_CALL ; Call MiProtectVirtualMemory jne @f mov edx,ImageHeader add ebx,dword ptr [ebx + 1] mov ecx,IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage[edx] cmp NtImageBase,ebx jae @f add ecx,NtImageBase cmp ecx,ebx ja valid_ @@: mov ebx,LastIp jmp step_ valid_: add ebx,5 mov edx,MiProtectVirtualMemory mov esi,LocalSst mov ecx,Sst sub ebx,NtImageBase sub esi,NtImageBase xor eax,eax mov dword ptr [edx],ebx mov dword ptr [ecx],esi jmp exit_ next_: cmp ebx,esi jb step_ error_: mov eax,STATUS_NOT_FOUND exit_: LEAVE_SEH ret QueryMiProtectVirtualMemory endp
freyr >замена начальных инструкций функции не в счет, уже никто давно так не балуется в ядре Хорошо знать >mov eax, large fs:124h >mov [eax+140h], 0 А вот с этим аккуратно. Адрес объекта текущего потока в структуре _KPCR определён архитектурно (т.е. для всей 32х-битной линейки NT его гарантированно можно получить по смещению 124h относительно сегмента fs), однако смещения полей в объекте потока могут меняться от версии к версии. Так что смещение PreviousMode в KTHREAD лучше, вероятно, всякий раз вычленять из функции ExGetPreviousMode().