Приветствую. В качестве защиты от перехвата решил попробовать перемапить модуль защищаемой библиотеки, задав отображению права только на чтение и исполнение. Но при попытке отмапить секцию на прежний адрес получаю ERROR_MAPPED_ALIGNMENT. В чём же проблема, ведь все адреса и смещения выровнены по размеру страницы? Код: https://hastebin.com/qoyekisudu.cpp
Не соблюдена гранулярность Код (C++): GetSystemInfo(&SysInfo); DWORD dwSysGran = SysInfo.dwAllocationGranularity; И ее используйте для выравнивания. Это касается всех 3х последних параметров MapViewOfFileEx, а не только одного размера секции, который вы пытались выровнять.
dwSysGran = 65 килобайт (0x10000). С таким выравниванием все адреса съезжают, а нужно замапить на прежние адреса
HoShiMin, ну так и делаем вывод - функция MapViewOfFileEx для этих целей неуместна. Переделывайте логику работы вашего ремаппера.
Отсюда следующий вопрос - чем можно мапить без ограничения на гранулярность? Секции должны отмапиться на прежние адреса, причём с разными правами, но гранулярность делает это невозможным. Как быть?
Мапь весь образ целиком: Код (C++): void start() { HMODULE hgdi32 = LoadLibrary(_T("gdi32")); RemapModule(hgdi32); HDC dc = GetDC(0); Ellipse(dc, 0, 0, 200, 200); return; } int RemapModule(HMODULE hModule) { PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)hModule; PIMAGE_NT_HEADERS NtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)hModule + DosHeader->e_lfanew); PIMAGE_OPTIONAL_HEADER OptionalHeader = &NtHeaders->OptionalHeader; ULONG ImageSize = OptionalHeader->SizeOfImage; // Копируем образ целиком в теневую память: HANDLE hShadowImage = CreateFileMapping(NULL, NULL, PAGE_EXECUTE_READWRITE, 0, ImageSize, NULL); PVOID ShadowImage = MapViewOfFile(hShadowImage, FILE_MAP_ALL_ACCESS, 0, 0, ImageSize); // Заголовки CopyMemory(ShadowImage, hModule, OptionalHeader->SizeOfHeaders); for (int i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) { PIMAGE_SECTION_HEADER pHdr = (PIMAGE_SECTION_HEADER)(OptionalHeader + 1) + i; CopyMemory((PBYTE)ShadowImage + pHdr->VirtualAddress, (PBYTE)hModule + pHdr->VirtualAddress, pHdr->Misc.VirtualSize); } // Отцепляем оригинальную секцию: VirtualFree(hModule, 0, MEM_RELEASE); UnmapViewOfFile(hModule); PVOID SectionMapping = MapViewOfFileEx(hShadowImage, FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, ImageSize, hModule); UnmapViewOfFile(ShadowImage); CloseHandle(hShadowImage); return 0; }
Хм, спасибо, так работает, но беспокоит одно: у вас весь модуль мапится с правами на запись (а смысл ремапинга именно в отключении возможности как-либо менять память кода). Для отображений образов можно оставить только READ и EXECUTE?
HoShiMin, Я, может, чего-то не догоняю, но как ты ремапингом защитишься от VirtualProtect (PAGE_READWRITE) с последующим изменением кода?
Я так сделал потому что я не обрабатывал каждую секцию. Правильно нужно задавать атрибуты в соответствии с атрибутами секций. Если ты укажешь только READ и EXECUTE, будет вызвано исключение если образ будет что-то писать в секцию с изначальными правами на запись, к примеру .data.
У VirtualProtect'a почему-то не получается изменить права перемапленного модуля Вот и вернулись к первому посту, где обрабатывал каждую секцию индивидуально и наткнулся на гранулярность... Как у системы получилось так размапить образ, не задумываясь о выравнивании и почему не могу сделать то же самое?
? Ну типа того: Код (C++): for (int i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) { DWORD old, attr; PIMAGE_SECTION_HEADER pHdr = (PIMAGE_SECTION_HEADER)(OptionalHeader + 1) + i; if (pHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) if (pHdr->Characteristics & IMAGE_SCN_MEM_READ) if (pHdr->Characteristics & IMAGE_SCN_MEM_WRITE) attr = PAGE_EXECUTE_READWRITE; else attr = PAGE_EXECUTE_READ; else if (pHdr->Characteristics & IMAGE_SCN_MEM_WRITE) attr = PAGE_EXECUTE_WRITECOPY; else attr = PAGE_EXECUTE; else if (pHdr->Characteristics & IMAGE_SCN_MEM_READ) if (pHdr->Characteristics & IMAGE_SCN_MEM_WRITE) attr = PAGE_READWRITE; else attr = PAGE_READONLY; else if (pHdr->Characteristics & IMAGE_SCN_MEM_WRITE) attr = PAGE_WRITECOPY; else attr = PAGE_NOACCESS; VirtualProtect((PBYTE)hModule + pHdr->VirtualAddress, pHdr->Misc.VirtualSize, attr, &old); }
Так проблема в том, что адреса отдельных секций не выравниваются по гранулярности и MapViewOfFileEx не может смапить их на прежние адреса - т.е., работает маппинг образа целиком, но не маппинг каждой секции по отдельности.
rmn, Thetrik Вот полный пример ремапа обычного куска памяти с последующей попыткой изменить её права (VirtualProtect возвращает ERROR_INVALID_PARAMETER): https://hastebin.com/ludafavuyi.cpp
Насколько мне известно чтобы замапить исполняемый образ нужно юзать SEC_IMAGE флаг в функции CreateFileMapping, но это не спасет от дальнейшего изменения через VirtualProtect. Просто я не пойму логику. Т.е. ты хочешь сделать чтобы образ не мог изменяться, но как быть тогда с writeble-секциями?
Writeable-секции оставить изменяемыми, но кодовые оставить ридонли. При условии, конечно, что там нет самомодифицирующегося кода.
HoShiMin, Как же вы до сих пор не поймёте, что защита, которую вы строете совершенно бессмысленна. Ваш код всегда может быть запущен под визором, что означает полный контроль над каждой инструкцией. Это в предельных случаях, так как отрицательно сказывается на профайле, но визор может запускаться по каким то событиям. Обычно такое не нужно и достаточно обычных техник. На кряклабе есть пример, вмпрот взят автоматикой - инструкции накоплены(и выборка данных) и собраны(графы, конструктор) воедино. В аттаче обсуждение такой защиты.
Защита, которую я строю, не предполагает защиту от визоров. У меня нет задачи защититься от всего на свете, но есть задача защититься от юзермодных инъекций (или запретить менять исполняемую память образа из юзермода, что сделает инъекции отчасти бессмысленными). Это достаточный минимум, чтобы быть уверенным, что никто из игроков его не обойдёт. Так как защищаю джава-процесс, нужно как-то отсечь подгрузку сторонних классов через JNI, который дёргают из внедряемой библиотеки (кроме перехвата некоторых функций из самой JVM). С такой постановкой задачи мне совсем не нужна защита от хардкорных способов обхода через визоры и нулевое кольцо - их никто применять не будет, т.к. никто в этом не разбирается. А обычные инъекции через CreateRemoteThread -> LoadLibrary делают все кому не лень, т.к. на просторах огромное количество готовых инъекторов. И в такой ситуации, когда нежелательно писать драйвер для защиты (т.к. у игроков зачастую нет админских прав, чтобы его установить), нужно придумать юзермодную защиту от юзермодных обходов. Не больше.
HoShiMin, Тоесть вам нужна не реальная защита, а создание видимости её получается. Невозможно создать полноценную защиту в нт, такая у неё архитектура - приложения не изолированы друг от друга и от км. Написать ровные сервисные фильтры - это слишком сложная для вас задача, это даже крупные корпорации не могут сделать командой. Архитектура нт позволяет делать что угодно с приложением, во время работы его или есчо до запуска(инфекты, инжекты етц). Сам же концепт защиты нт - ограничение прав. Если есть доступ к открытию обьекта, то с ним можно делать что угодно. Но реально же это бессмысленно, так как способы поднятия прав доступны даже детям. Есть два варианта реализации. 1. Виртуализация кода, таким образом даже при доступе к коду нельзя вмешаться в механизмы его работы, так как для этого необходима девиртуализация - выяснение логики работы. 2. Системные анклавы. Но для этого нужно реализовать свой визор. Остальное всё бесперспективно.
Я ведь и не спорю. Но как быть в ситуации, когда ставить драйвера нельзя, а защититься нужно? Вот и получается, что что-то юзермодное можно сделать через ремаппинг. И с ним такой облом, что гранулярность делает невозможным накрывать модули. Или всё же можно размапить образ руками без SEC_IMAGE в обход требований гранулярности?
HoShiMin, Я не вполне понимаю что вам нужно. Ремап проекции юзался в качестве защиты, основываясь на том, что область памяти, которая является не файловой проекцией изменить из юм невозможно, не ремапив её повторно, но такой ремап будет отображён на вторую проекцию, которая служит для такого детекта ремапа. То что у вас при проецировании ошибка - я не помню, но это проблема была решена, поищите сурки, толи самой этой защиты, то ли это был загрузчик(LWE), либо крякми. Подробности я не помню.