2016-12-02 5 views
3

Я хотел бы выделить буфер, который я могу выполнить на Win32, но у меня есть исключение в visual studio, потому что функция malloc возвращает не исполняемую зону памяти. Я прочитал, что есть флаг NX для отключения ... Моя цель - преобразовать байт-код в asm x86 на лету с сохранением производительности.Как выделить буфер исполняемой памяти?

Помогает ли мне кто-нибудь?

JS

+1

Вы, вероятно, хотели бы взглянуть на [asmJit] (https://github.com/asmjit/asmjit), который является бесплатным кодом, который выполняет весь тяжелый подъем для части управления сборкой/памятью записи JIT, включая установку битов защиты памяти, чтобы вы могли выполнить свой код. – BeeOnRope

ответ

7

Для этого не используется malloc. Зачем вам все равно в программе на C++? Однако вы также не используете new для исполняемой памяти. Для резервирования памяти есть функция VirtualAlloc для Windows, которую вы затем отмечаете как исполняемый с помощью функции VirtualProtect, применяя, например, флаг PAGE_EXECUTE_READ.

Когда вы это сделали, вы можете направить указатель на выделенную память на соответствующий тип указателя функции и просто вызвать функцию. Не забудьте позвонить по телефону VirtualFree, когда все будет готово.

Вот некоторые очень простой пример кода, без обработки ошибок или других проверок здравомыслие, чтобы показать вам, как это может быть достигнуто в современном C++ (программа печатает 5):

#include <windows.h> 
#include <vector> 
#include <iostream> 
#include <cstring> 

int main() 
{ 
    std::vector<unsigned char> const code = 
    { 
     0xb8,     // move the following value to EAX: 
     0x05, 0x00, 0x00, 0x00, // 5 
     0xc3     // return what's currently in EAX 
    };  

    SYSTEM_INFO system_info; 
    GetSystemInfo(&system_info); 
    auto const page_size = system_info.dwPageSize; 

    // prepare the memory in which the machine code will be put (it's not executable yet): 
    auto const buffer = VirtualAlloc(nullptr, page_size, MEM_COMMIT, PAGE_READWRITE); 

    // copy the machine code into that memory: 
    std::memcpy(buffer, code.data(), code.size()); 

    // mark the memory as executable: 
    DWORD dummy; 
    VirtualProtect(buffer, code.size(), PAGE_EXECUTE_READ, &dummy); 

    // interpret the beginning of the (now) executable memory as the entry 
    // point of a function taking no arguments and returning a 4-byte int: 
    auto const function_ptr = reinterpret_cast<std::int32_t(*)()>(buffer); 

    // call the function and store the result in a local std::int32_t object: 
    auto const result = function_ptr(); 

    // free the executable memory: 
    VirtualFree(buffer, 0, MEM_RELEASE); 

    // use your std::int32_t: 
    std::cout << result << "\n"; 
} 

Это очень необычно по сравнению к нормальному управлению памятью на С ++, но не совсем ракетостроению. Жесткая часть состоит в том, чтобы получить правильный код машины. Обратите внимание, что мой пример здесь - очень простой x64-код.

+1

Причина, по которой вы должны использовать malloc в программе на C++, - это распределение динамической памяти из вашей кучи. Прекрасно нормально и нормально. –

+0

@KyleSweet: в C++ вы используете «новый» (либо «обычный», либо новый, например, «std :: allocator») для динамического выделения из свободного хранилища (а не кучи). Вы можете технически использовать 'malloc', но тогда вы эффективно пишете C, а не (идиоматический) C++. Для вопроса OP важно также различие между «VirtualAlloc» и «malloc». –

+1

Я просто отвечал на заданный вами вопрос. Если все, что вам нужно, - это быстрая и грязная память из кучи, не бойтесь использовать malloc! –

4

Как указано в documentation для VirtualAlloc

flProtect [в]

Защита памяти для региона страниц, чтобы быть выделены. Если страницы фиксируются, вы можете указать любую из констант защиты памяти.

один из них:

PAGE_EXECUTE 0x10 Позволяет выполнять доступ к совершенной области страниц. Попытка написать в зафиксированный регион приводит к нарушению доступа. Этот флаг не поддерживается функцией CreateFileMapping.

PAGE_EXECUTE_READ 0x20 Включает выполнение или доступ только для чтения к зафиксированному региону страниц. Попытка написать в зафиксированный регион приводит к нарушению доступа. Windows Server 2003 и Windows XP: этот атрибут не поддерживается функцией CreateFileMapping до Windows XP с пакетом обновления 2 (SP2) и Windows Server 2003 с пакетом обновления 1 (SP1).

PAGE_EXECUTE_READWRITE 0x40 Включает возможность выполнения, доступ только для чтения или чтение/запись в выделенный регион страниц. Windows Server 2003 и Windows XP: этот атрибут не поддерживается функцией CreateFileMapping до Windows XP с пакетом обновления 2 (SP2) и Windows Server 2003 с пакетом обновления 1 (SP1).

и так далее от here

+0

Похоже, что это относится к * страницам * памяти. Здесь уместно? –

+0

Да. Если вам нужно управлять режимами, вам нужно выделить _pages_. – bmargulies

-2

Во время компиляции, компоновщик будет организовывать объем памяти вашей программы путем выделения памяти в секцию данных и секцию коды. ЦП будет следить за тем, чтобы значение счетчика программ (жесткого регистра процессора) оставалось в пределах раздела кода, или процессор будет вызывать аппаратное исключение для нарушения границ памяти. Это обеспечивает некоторую безопасность, гарантируя, что ваша программа выполняет только действительный код. Malloc предназначен для распределения памяти данных. Ваше приложение имеет кучу, а размер кучи устанавливается компоновщиком и помечен как память данных. Таким образом, во время выполнения malloc просто захватывает часть виртуальной памяти из вашей кучи, которая всегда будет данными.

Надеюсь, это поможет вам лучше понять, что происходит, хотя этого может быть недостаточно, чтобы получить вас там, где вам нужно. Возможно, вы можете предварительно выделить «кучу кода» или пул памяти для вашего кода, созданного во время выполнения. Вам, вероятно, придется столкнуться с компоновщиком, чтобы выполнить это, но я не знаю ни одной детали.

+0

Разрешения на чтение/запись/выполнение на страницах * динамически * выделенной памяти не имеют никакого отношения к компоновщику. Если вам нужны некоторые * статически выделенные * write + execute pages, вы можете сделать это с помощью скриптов компоновщика или файлов GNU C '__attribute__' в массиве. –

+1

Кроме того, защита памяти не работает, проверяя, что счетчик программ (RIP на x86-64) остается в определенных диапазонах. Именно так работала сегментация (с базой/лимитом), но x86-64 даже не поддерживает ограничения сегментов в 64-битном режиме. Защита памяти выполняется на каждой странице, с битами в таблице страниц (устанавливается ОС). См. Https://stackoverflow.com/questions/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with- для диаграммы. –

3

Расширения ответа выше, хорошая практика:

  • Выделяет память с VirtualAlloc и чтением-запись-доступ.
  • Заполните эту область с кодом
  • Изменение, что защита региона с VirtualProtect для выполнения читаемый-доступа
  • прыжок/вызвать точку входа в этом регионе

Так может выглядеть следующим образом:

adr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 
// write code to the region 
ok = VirtualProtect(adr, size, PAGE_EXECUTE_READ, &oldProtection); 
// execute the code in the region 
+1

Согласно спецификации VirtualProtect не удастся, если последний параметр равен NULL. Использование указателя на (фиктивная) выходная переменная является обязательным. – user3042599

+0

@ пользователь3042599: Thx. Починил это. – zx485

Смежные вопросы