Затриггерить Copy-on-write из ядра

Тема в разделе "WASM.NT.KERNEL", создана пользователем HoShiMin, 12 июл 2020.

  1. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.427
    Адрес:
    Россия, Нижний Новгород
    Допустим, мы хотим поставить хук на библиотечную юзермодную функцию из ядра и эта функция находится в системной библиотеке.

    Есть вариант вызвать ZwProtectVirtualMemory, но эта функция не экспортируется на Win7 - хотелось бы обойтись без её поиска по сигнатурам.

    Другой, более правильный и предпочтительный вариант - MmMapLockedPagesSpecifyCache + MmProtectMdlSystemAddress.
    Однако, т.к. библиотека разделяется между множеством процессов, мы отобразим "общую" память - следовательно, изменения одновременно будут видны во всех процессах.

    Необходимо как-либо затриггерить Copy-On-Write (и очень желательно сделать это "легальным" способом, обойдясь без вызова неэкспортируемых функций).

    В структуре PTE есть поле AVL, состоящее из трёх бит, которые ОС может использовать для своих нужд.
    В Win10 x64 2004 PTE определена следующим образом:
    Код (Text):
    1.  
    2. struct _HARDWARE_PTE
    3. {
    4.     unsigned __int64 Valid : 1;
    5.     unsigned __int64 Write : 1;
    6.     unsigned __int64 Owner : 1;
    7.     unsigned __int64 WriteThrough : 1;
    8.     unsigned __int64 CacheDisable : 1;
    9.     unsigned __int64 Accessed : 1;
    10.     unsigned __int64 Dirty : 1;
    11.     unsigned __int64 LargePage : 1;
    12.     unsigned __int64 Global : 1;
    13.     unsigned __int64 CopyOnWrite : 1;
    14.     unsigned __int64 Prototype : 1;
    15.     unsigned __int64 reserved0 : 1;
    16.     unsigned __int64 PageFrameNumber : 36;
    17.     unsigned __int64 reserved1 : 4;
    18.     unsigned __int64 SoftwareWsIndex : 11;
    19.     unsigned __int64 NoExecute : 1;
    20. };
    21.  
    Поле AVL отведено под поля CopyOnWrite, Prototype и reserved0.
    Чтобы пометить страницу, как готовую к CoW, система выставляет бит CopyOnWrite, но не выставляет бит Write: страница фактически остаётся Read[Execute]Only.
    Дальнейшая попытка записи в эту страницу приведёт к генерации исключения и обработчик, видя бит CopyOnWrite, подгрузит страницу в частный рабочий набор процесса.
    И действительно, после "ручного" триггера (взвести бит CoW, произвести запись в страницу) мы увидим, что физический адрес страницы изменился и данные в ней можно смело менять.

    upload_2020-7-12_1-10-25.png

    Однако, спустя некоторое время после завершения процесса, система падает в синий экран с ошибкой MEMORY_MANAGEMENT (1a).

    Трейс:
    Код (Text):
    1.  
    2. nt!DbgBreakPointWithStatus
    3. nt!KiBugCheckDebugBreak+0x12
    4. nt!KeBugCheck2+0x946
    5. nt!KeBugCheckEx+0x107
    6. nt!MiGetTopLevelPfn+0x1c664b
    7. nt!MiCapturePfnVm+0xdf
    8. nt!MiProcessCrcList+0x226
    9. nt!MiCombineAllPhysicalMemory+0x30d
    10. nt!MiCombineIdenticalPages+0x214
    11. nt!NtSetSystemInformation+0x59a
    12. nt!KiSystemServiceCopyEnd+0x25
    13. ntdll!NtSetSystemInformation+0x14
    14. sysmain!PfsCombineWorker+0x1b0
    15. KERNEL32!BaseThreadInitThunk+0x14
    16. ntdll!RtlUserThreadStart+0x21
    17.  
    Конкретное место краша ничего не проясняет:

    upload_2020-7-12_0-58-5.png

    Может, у кого-то есть идеи, как по-другому (или более правильно) затриггерить CoW?
    Или как почистить за собой следы, чтобы валидатор памяти не валил систему в синий экран?
     
    Последнее редактирование: 12 июл 2020