10.13 High Sierra OSX - Python mprotect всегда дает сбой при предоставлении разрешения exec с ENOMEM

Фон:

Написание доказательства концепции, которая включает в себя выполнение машинного кода в программе Python. Чтобы сделать это на OSX, мне пришлось использовать ctypes и libc.dylib и следующие вызовы функций:

(С отключенным SIP)

  1. valloc для выделения выровненной памяти
  2. mprotect для предоставления разрешения wrx на выделенную память
  3. memmove для копирования исполняемого кода в выделенную память; бросать; и выполнить...

Проблема:

Проблема возникает при вызове функции mprotect, где она всегда возвращает -1 в случае сбоя.

Сценарий: (логика почти идентична системе Linux, так как они оба семейства posix)

import ctypes

buf = "machine code..."
libc = cytpes.CDLL('libc.dylib')
size = len(buf)
buf_ptr = ctypes.c_char_p(buf)

# allocate aligned memory space
alloc_space = ctypes.c_void_p(ctypes.valloc(size))

# this always evaluates true, and mprotect fails every attempt
if 0 != libc.mprotect(alloc_space, size, 1 |2 |4):
  print "mprotect failed"

ctypes.mmove(alloc_space, buf_ptr, size)

Теперь mmove завершится ошибкой с сообщением об ошибке segfault (b / c записывает пространство памяти, которое, вероятно, имело только право на чтение), и программа приходит к хаосу...

Проблема с mprotect, этот метод очень хорошо работает в Linux, теперь я вижу, что результаты для Mac OSX очень разные

Вопрос:

Есть ли в Mac OSX дополнительные функции безопасности (даже с отключенным SIP), ограничивающие тип операции mprotect? И если так, как можно обойти это?

ОБНОВИТЬ:

Согласно @DietrichEpp, предложенному в комментариях, использование use_errno=True для ctypes.CDLL вызвало ошибку errno. Это оценивается как errno: 12, Невозможно выделить память. Это errno - значение для ENOMEM на странице руководства mprotect.

Хотя на странице руководства было несколько ENOMEM, я подозреваю, что это последний сценарий: (b/c не было ошибки при вызове valloc)

   ENOMEM Changing the protection of a memory region would result in the
          total number of mappings with distinct attributes (e.g., read
          versus read/write protection) exceeding the allowed maximum.
          (For example, making the protection of a range PROT_READ in
          the middle of a region currently protected as
          PROT_READ|PROT_WRITE would result in three mappings: two
          read/write mappings at each end and a read-only mapping in the
          middle.)

Я подозреваю, что osx имеет особые ограничения и установил максимальные сопоставления для каждого процесса, следовательно, добавив больше разрешений, новое сопоставление того же процесса превысит такой максимальный предел (сколько сопоставлений с привилегиями exec/write на процесс). Если мои предположения были верны, как мы можем обойти это?

1 ответ

Решение

Справочные страницы Apple больше не находятся в сети, но обратитесь к справочной странице POSIX для mprotect:

Поведение этой функции не определено, если сопоставление не было установлено при вызове mmap ().

Похоже, что Linux более щадителен в этом отношении и позволяет вам вызывать mprotect () для любой памяти, которую вы хотите, более или менее. Дарвин более строг и требует от вас использования памяти из mmap(), если вы хотите вызвать mprotect(). Это одна из причин, почему стоит прочитать всю справочную страницу от начала до конца.

Если вы думаете об этом, это разумное требование. Память, предоставляемая valloc (), управляется распределителем и должна быть позже возвращена распределителю с помощью free (), а mprotect () в некотором смысле обходит спину распределителя и изменяет способ работы памяти. Это не относится к mmap () и munmap(), которые относятся к тому же семейству системных вызовов, что и mprotect().

Другие вопросы по тегам