Имитация (отложенной) памяти NAND в Windows

#windows #winapi #simulation #virtual-memory

#Windows #winapi #Симуляция #виртуальная память

Вопрос:

Я запускаю симуляцию встроенного ПО в DLL, которая имитирует NAND (256 МБ или 1 ГБ). Я хочу избежать выделения памяти для этого в куче и вместо этого выделить с использованием виртуальной памяти.

Память изначально должна быть очищена до 0xFF (как и NAND). Однако я не хочу платить за эту инициализацию (и не фиксировать недоступные страницы). Поэтому в идеале она должна выделяться только при доступе. И мне не нужно сохранять данные после завершения моделирования.

Первоначальные идеи

  1. VirtualAlloc. Не уверен, но думаю, возможно, можно было бы использовать защитную страницу, а затем перехватить исключение при первом доступе. Не уверен, что идеально, чтобы DLL обрабатывала такие исключения SEH? Или есть способ получше?

  2. Создайте большой файл, инициализированный в 0xFF. Затем отображение файла с копированием при записи. Кто-нибудь знает, возможно ли создать файл с обратным вызовом для предоставления исходных данных?

Думаю, вероятно, 1) как поступить, но интересно, действительно ли это лучший вариант.

Редактировать: 3) Я придумал другой метод, который может избежать обработки исключений, а также позволяет избежать создания огромного файла: создайте файл того же размера, что и dwAllocationGranularity (обычно 64 КБ). Заполнить значением 0xFF. Затем создайте несколько представлений для копирования при записи в смежной памяти, используя MapViewOfFileEx FILE_MAP_COPY (после начального VirtualAlloc / VirtualFree, чтобы получить подходящий базовый адрес, который, мы можем надеяться, выделить для смежных представлений). Необходимо протестировать это немного более полно — небольшое беспокойство по поводу потенциальных гонок потоков.. На самом деле я использую только один поток, но CRT тоже запускает несколько. Это означает, что любой код, который считывает только виртуальную NAND, также не приводит к фиксации всех страниц.

Комментарии:

1. 1 — действительно хорошее решение. но еще лучше использовать MEM_RESERVED только выделение, обрабатывать исключения при доступе и заполнять память 0xFF , но вам нужен VEH в целом, а не SEH

2. да, 3 также хорошее решение, и здесь, конечно, нет никакой гонки потоков. почему вы решили, что это может быть и как вы это видите? в любом случае исключение будет при первой записи на страницу, но будет обработано в ядре

3. @RmMm — возможная гонка между вызовами VirtualFree и несколькими вызовами MapViewOfFileEx. Сначала я вызываю VirtualAlloc, чтобы получить подходящий базовый адрес, а затем сразу освобождаю VirtualFree (в противном случае вызовы MapoViewOfFileEx завершаются неудачей). Я думаю, я просто попробую это несколько раз, если произойдет сбой в какой-либо из функций сопоставления файлов. Другим вариантом, я полагаю, было бы убедиться, что все остальные потоки недоступны для выполнения во время этого раздела кода.

4. да, это возможно. но это еще не этап инициализации. однако я бы выбрал 1 вариант

Ответ №1:

да, в принципе, 1 — лучшее решение. только я буду вносить следующие изменения — использовать VEH вместо SEH — обработчик SEH будет вызываться, только если вы обращаетесь к памяти внутри него, когда в случае, если VEH — доступ может быть из любого контекста и потока. и вместо этого используйте защитную страницу, я изначально резервирую только область памяти без реального выделения. таким образом, любой доступ к области памяти приводит к исключению, вы обрабатываете его в памяти с фиксацией и заполняете 0xFF шаблоном. демонстрационный код

 PVOID g_NandBegin;
SIZE_T g_NandSize = 0x1000000;

LONG NTAPI Vex(::PEXCEPTION_POINTERS ExceptionInfo)
{
    ::PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;

    if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION amp;amp;
        ExceptionRecord->NumberParameters > 1)
    {
        PVOID pv = (PVOID)ExceptionRecord->ExceptionInformation[1];

        if ((ULONG_PTR)pv - (ULONG_PTR)g_NandBegin < g_NandSize)
        {
            SIZE_T RegionSize = 1;
            if (0 <= NtAllocateVirtualMemory(NtCurrentProcess(), amp;pv, 0, amp;RegionSize, MEM_COMMIT, PAGE_READWRITE))
            {
                RtlFillMemoryUlong(pv, RegionSize, MAXULONG);
                return EXCEPTION_CONTINUE_EXECUTION;
            }
        }
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

void dc()
{
    if (PVOID pv = AddVectoredExceptionHandler(TRUE, Vex))
    {
        if (g_NandBegin = VirtualAlloc(0, g_NandSize, MEM_RESERVE, PAGE_READWRITE))
        {
            ULONG seed = ~GetTickCount();
            int n = 0x100;

            do 
            {
                if (*(UCHAR*)((PBYTE)g_NandBegin   (((ULONG64)RtlRandomEx(amp;seed) * g_NandSize) >> 32)) != 0xFF)
                {
                    __debugbreak();
                }
            } while (--n);

            VirtualFree(g_NandBegin, 0, MEM_RELEASE);
        }
        RemoveVectoredExceptionHandler(pv);
    }
}
  

Комментарии:

1. Спасибо r.e. векторному обработчику исключений.. Попробовал это, и это работает хорошо. Придумали другой, немного отличающийся подход — обновили вопрос.