Похоже испытал на себе всю прелесть DEP (Data Execution Prevention) Мой старый код, который отлично работал в 32-х битном ядре отказывается выполняться на х64. подозреваю, что последовательность IoAllocateMdl MmProbeAndLockPages MmMapLockedPagesSpecifyCache резервирует MDL, по-умолчанию запрещенный к выполнению.. на х32 (без PAE) NX-бит не работает, а вот на х64 он включен.. вобще, задача такая: промапить кусок кода из адресного пространства драйвера в адресное пространство какого-то процесса. для этого сначала вызываю KeStackAttachProcess затем создаю новый MDL: IoAllocateMdl MmProbeAndLockPages MmMapLockedPagesSpecifyCache - отдает адрес в юзермоде, на который потом прыгает для выполнения юзермодный процесс.. так вот, в х32 - этот промапленный кусок кода замечательно выполняется.. а в х64 - юзермод код выкидывает исключение на первой же инструкции промапленного кода: Problem Event Name: BEX64 Exception Code: C0000005 думаю, что это как-раз DEP срабатывает. вопрос в следующем: как изменить атрибуты этого куска памяти - пометить его "выполняемым". Олег
Cr4sh Нет, мне нужен кернел-аналог этой ф-ции.. (причем присутствующий в экспортах ntoskrnl.exe, т.к. для доступа к сервисам ядра я разбираю ExportTable ntoskrnl.exe).. мне нужно изменять этот атрибут из ринг0
возможно решением будет также разбор PDE/PTE вручную, и отключение NX-бита в задействованных страницах.. но хотелось бы чего-то попроще..
ratix Ага, сталкивался с такой херней. Выделяешь память и меняешь уровень доступа к ней. Либо меняй биты вручную. либо как вариант сделать аттач к процессу и заказать память с нужными аттрибутами через ZwAllocateVirtualMemory
TermoSINteZ вот в том то и вопрос: как менять уровень доступа из ядра (NtProtectVirtualMemory/ZwAllocateVirtualMemory - юзермодные ф-ции) Есть указатель на промапленную память, и MDL. неужели нет способа кроме правки битов вручную в каталогах страниц?
ratix С какого это перепугу что они юзермодные? Если они есть в Ntdll.dll это не значит что их нет в ядре! Если ты хочешь тока через мдл и таблицы страниц... думаю нет других способов. Ихмо проще приаттачиться и заюзать. Ибо все равно никто это дело не просечет.
нашел ф-цию MmProtectMdlSystemAddress но она почему-то работает тока для памяти промапленной в ядерное адресное просранство (вызов MmMapLockedPagesSpecifyCache(pMdl, KernelMode, ...)) если же память замаплена в юзермод (с помощью MmMapLockedPagesSpecifyCache(pMdl, UserMode, ...), то MmProtectMdlSystemAddress возвращает ошибку 0хс0000019 как же быть.. так не хочется разбирать страницы вручную.. TermoSINteZ помоему атачиться только для того, чтобы изменит атрибуты - слишком сложный путь.. разбор и правка PDE/PTE и то проще..
Код (Text): ULONG ExecuteFlags = MEM_EXECUTE_OPTION_ENABLE; NtSetInformationProcess( NtCurrentProcess(), // (HANDLE)-1 ProcessExecuteFlags, // 0x22 &ExecuteFlags, // ptr to 0x2 sizeof(ExecuteFlags)); // 0x4 http://www.securitylab.ru/analytics/263899.php подобная тема недавно на кряклабе обсуждалась http://cracklab.ru/f/index.php?action=vthread&forum=6&topic=10176
и будет не гуд а бсод =) int2eh на х64 теперь только в юзере вы же не одни живёте всё уже написано =) (ц) моЁ =) Код (Text): int NXFree(DWORD_PTR ptr) // 64-bit not PAE! // // disables NX bit for page pointed by ptr on x64 platform // { DWORD_PTR* pPML4,* pPDP,* pPD,* pPT, PML4e, PDPe, PDe, PTe; PHYSICAL_ADDRESS phys; KDPRINT(("\nNXFree goes.. param = 0x%p\n", ptr)); pPML4 = (DWORD_PTR*) GetPML4Base(); if (pPML4) { KDPRINT((" pPML4 = 0x%p\n", pPML4)); phys.QuadPart = (LONGLONG)((ptrdiff_t)pPML4+ (ptrdiff_t)(VM_GET_PML4OFFSET(ptr)*sizeof(DWORD_PTR))); KDPRINT((" PML4 index = 0x%p, PML4 phys addr = 0x%p\n", VM_GET_PML4OFFSET(ptr), phys.QuadPart)); pPML4 = (DWORD_PTR*)MmMapIoSpace(phys, sizeof(DWORD_PTR), MmNonCached); if (!pPML4) { KDPRINT(("*** ERROR: PML4e phys map fails")); return 0; } PML4e = *pPML4; MmUnmapIoSpace((PVOID)pPML4, sizeof(ptrdiff_t)); if (PML4e) { KDPRINT((" PML4e = 0x%p\n", PML4e)); phys.QuadPart = (LONGLONG)((ptrdiff_t)(PML4e & VM_MAP_BASE_POINTER) + (ptrdiff_t)(VM_GET_PDPOFFSET(ptr)*sizeof(DWORD_PTR))); KDPRINT((" PDP index = 0x%p, PDP phys addr = 0x%p\n", VM_GET_PDPOFFSET(ptr), phys.QuadPart)); pPDP = MmMapIoSpace(phys, sizeof(DWORD_PTR), MmNonCached); if (!pPDP) { KDPRINT(("*** ERROR: PDPe phys map fails")); return 0; } PDPe = *pPDP; MmUnmapIoSpace(pPDP, sizeof(DWORD_PTR)); if (PDPe) { KDPRINT((" PDPe = 0x%p\n", PDPe)); phys.QuadPart = (LONGLONG)((ptrdiff_t)(PDPe & VM_MAP_BASE_POINTER) + (ptrdiff_t)(VM_GET_PDOFFSET(ptr)*sizeof(DWORD_PTR))); KDPRINT((" PD intex = 0x%p, PD phys addr = 0x%p\n", VM_GET_PDOFFSET(ptr), phys.QuadPart)); pPD = (DWORD_PTR *)MmMapIoSpace(phys, sizeof(DWORD_PTR), MmNonCached); if (!pPD) { KDPRINT(("*** ERROR: PDe phys map fails")); return 0; } PDe = *pPD; KDPRINT((" PDe = 0x%p\n", PDe)); // // checking for 4k/2m pages // if (PDe & PDE_PS) { // 2M pages KDPRINT(("2 megabyte page detected\n")); if (PDe & NX_FLAG) { *pPD = PDe & (~NX_FLAG); KDPRINT(("New PTE = 0x%p\n", *pPD)); } else { KDPRINT(("page is already executable\n")); } } else { // 4K pages if (PDe) { phys.QuadPart = (LONGLONG)((ptrdiff_t)(PDe & VM_MAP_BASE_POINTER) + (ptrdiff_t)(VM_GET_PTOFFSET(ptr)*sizeof(DWORD_PTR))); KDPRINT((" PT index = 0x%p, PT phys addr = 0x%p\n", VM_GET_PTOFFSET(ptr), phys.QuadPart)); pPT = (DWORD_PTR *)MmMapIoSpace(phys, sizeof(DWORD_PTR), MmNonCached); if (!pPT) { KDPRINT(("*** ERROR: PTe phys map fails")); return 0; } PTe = *pPT; if (PTe) { KDPRINT((" PTe = 0x%p\n", PTe)); // // clear NX bite // if (PTe & NX_FLAG) { WriteFlagClear(); *pPT = PTe & (~NX_FLAG); WriteFlagRestore(); KDPRINT(("New PTE = 0x%p\n", *pPT)); } } else { KDPRINT(("*** ERROR: PTe is empty")); return 0; } MmUnmapIoSpace(pPT, sizeof(DWORD_PTR)); } else { KDPRINT(("*** ERROR: PDe is empty")); return 0; } //end 4k } KDPRINT(("unmap pPD...\n")); MmUnmapIoSpace(pPD, sizeof(DWORD_PTR)); } else { KDPRINT(("*** ERROR: PDPe is empty")); return 0; } } else { KDPRINT(("*** ERROR: PML4e is empty")); return 0; } } else { KDPRINT(("*** ERROR: pPML4 is empty")); return 0; } return 1; } Кстати сколько сталкивался зачастую при выделении памяти при ExAllocatePool память не содержит атрибута NX, а вот стек почти всегда его содержит
ыы, я заблуждался 1) В ядре нет обрадотчика такого прерывания вообще, вызов его ведёт прямиком в KiUnexpectedInterrupt. Zw кернел заглушки для вызова Nt функций просто jmp'аются на KiServiceInternal 2) int2eh вызывает нарушени доступа не только в кенел но и юзермоде даже в 32-х битных приложениях. Вызов из х32 кода системного сервиса выглядит примерно так: Код (Text): .text:7D61CFA8 mov eax, 52h ; NtCreateFile .text:7D61CFAD xor ecx, ecx .text:7D61CFAF lea edx, [esp+arg_0] .text:7D61CFB3 call large dword ptr fs:0C0h по адресу fs:[c0h]: Код (Text): 78B81910 EA 2C3CB878 3300 JMP FAR 0033:78B83C2C ; Far jump чесно говоря я хз как проследить куда отсуда передаётся управление мой kd говорит : "The context is partially valid. Only x86 user-mode context is available." и отказывается трейсить :-\ небезивестный skywing'а говорит: и приводит трейс: Код (Text): ntdll_777f0000!ZwWaitForMultipleObjects+0xe: 00000000`7783aebe 64ff15c0000000 call dword ptr fs:[0C0h] 0:000:x86> t wow64cpu!X86SwitchTo64BitMode: 00000000`759c31b0 ea27369c753300 jmp 0033:759C3627 0:012:x86> t wow64cpu!CpupReturnFromSimulatedCode: 00000000`759c3627 67448b0424 mov r8d,dword ptr [esp] Кстати заметил _любопытную особенность_, при тестинге на варе (5.5.2) в х64 коде если вызвать int 2eh варя навернётся с "*** Virtual machine kernel stack fault (hardware reset) ***" я не знаю почему, весёлая антиотладка, как считаете? )