#windows #winapi #simulation #virtual-memory
#Windows #winapi #Симуляция #виртуальная память
Вопрос:
Я запускаю симуляцию встроенного ПО в DLL, которая имитирует NAND (256 МБ или 1 ГБ). Я хочу избежать выделения памяти для этого в куче и вместо этого выделить с использованием виртуальной памяти.
Память изначально должна быть очищена до 0xFF (как и NAND). Однако я не хочу платить за эту инициализацию (и не фиксировать недоступные страницы). Поэтому в идеале она должна выделяться только при доступе. И мне не нужно сохранять данные после завершения моделирования.
Первоначальные идеи
-
VirtualAlloc. Не уверен, но думаю, возможно, можно было бы использовать защитную страницу, а затем перехватить исключение при первом доступе. Не уверен, что идеально, чтобы DLL обрабатывала такие исключения SEH? Или есть способ получше?
-
Создайте большой файл, инициализированный в 0xFF. Затем отображение файла с копированием при записи. Кто-нибудь знает, возможно ли создать файл с обратным вызовом для предоставления исходных данных?
Думаю, вероятно, 1) как поступить, но интересно, действительно ли это лучший вариант.
Редактировать: 3) Я придумал другой метод, который может избежать обработки исключений, а также позволяет избежать создания огромного файла: создайте файл того же размера, что и dwAllocationGranularity (обычно 64 КБ). Заполнить значением 0xFF. Затем создайте несколько представлений для копирования при записи в смежной памяти, используя MapViewOfFileEx FILE_MAP_COPY (после начального VirtualAlloc / VirtualFree, чтобы получить подходящий базовый адрес, который, мы можем надеяться, выделить для смежных представлений). Необходимо протестировать это немного более полно — небольшое беспокойство по поводу потенциальных гонок потоков.. На самом деле я использую только один поток, но CRT тоже запускает несколько. Это означает, что любой код, который считывает только виртуальную NAND, также не приводит к фиксации всех страниц.
Комментарии:
1. 1 — действительно хорошее решение. но еще лучше использовать
MEM_RESERVED
только выделение, обрабатывать исключения при доступе и заполнять память0xFF
, но вам нужен VEH в целом, а не SEH2. да, 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. векторному обработчику исключений.. Попробовал это, и это работает хорошо. Придумали другой, немного отличающийся подход — обновили вопрос.