Использование mprotect, чтобы сделать текстовый сегмент доступным для записи в macOS

По сути, это то, что я пытаюсь сделать,

#include <sys/mman.h>

int zero() {
    return 0;
}

int main(int argc, const char *argv[]) {
    return mprotect((void *) &zero, 4096, PROT_READ | PROT_WRITE);
}

поэтому я, по сути, пытаюсь сделать код доступным для записи. Это не работает в текущей macOS (Catalina 10.15.2), она просто возвращает -1 и устанавливает errno в EACCES, что, насколько мне известно, связано с отсутствием прав / подписи кода. Я нашел право, которое мне нужно установить, но я понятия не имею, как это сделать и как на самом деле его подписать.

Если я запустил codesign -d --entitlements :- <path_to_app>, он не сработает с code object is not signed at all, хотя я некоторое время пытался настроить подпись в Xcode (у меня есть сертификат и т. Д.). Итак, как мне это сделать? На самом деле подписание в Xcode неочевидно, так что я довольно невежественен.


person somethingunderscore    schedule 12.03.2020    source источник
comment
Это проблема PROT_WRITE в MacOS Catalina. Мы не могли исправить это, передав все флаги в правах.   -  person tobe    schedule 15.05.2020
comment
У нас есть проверка кодовой подписи со всеми правами, такими как com.apple.security.cs.allow-jit, com.apple.security.cs.allow-unsigned-executable-memory и com.apple.security.cs.disable-executable-page-protection, но это не сработало. Это может быть проблема MacOS Catalina, и разработчики из Apple могли ее исправить.   -  person tobe    schedule 18.05.2020


Ответы (1)


Это не окончательный ответ, но это обходной путь.

Ваша проблема вызвана изменениями компоновщика (ld64) в macOS Catalina. Значение по умолчанию атрибута max_prot сегмента __TEXT в заголовке Mach-O было изменено.

Ранее значение по умолчанию max_prot было 0x7 (PROT_READ | PROT_WRITE | PROT_EXEC).
Значение по умолчанию теперь изменено на 0x5 (PROT_READ | PROT_EXEC).

Это означает, что mprotect не может сделать любую область, которая находится внутри __TEXT, доступной для записи.

Теоретически это должно быть решено с помощью флага компоновщика -segprot __TEXT rwx rx, но это не так. Начиная с Catalina, поле max_prot игнорируется. Вместо этого для max_prot установлено значение init_prot (см. здесь).

В довершение ко всему, init_prot нельзя установить на rwx из-за того, что macOS отказывается выполнять файл с атрибутом __TEXT(init_prot) с возможностью записи.

Грубый обходной путь - вручную изменить и установить __TEXT(max_prot) на 0x7 после связывания.

printf '\x07' | dd of=<executable> bs=1 seek=160 count=1 conv=notrunc

Поскольку этот фрагмент кода зависит от смещения __TEXT(max_prot), которое жестко запрограммировано на 0xA0, в качестве альтернативы я создал drop -in replace / wrapper для ld, который учитывает параметр max_prot segprot.

person Elliott Darfink    schedule 20.05.2020
comment
Вы можете обойти проверку Каталины dyld: malformed mach-o image: __TEXT segment maps start of file but is writable с помощью описанного здесь обходного пути stackoverflow.com/a/60505259/5329717 - person Kamil.S; 21.05.2020
comment
Это интересный лакомый кусочек! В случае mprotect я бы сказал, что лучше вместо этого изменить атрибут max_prot, но приятно знать, что есть альтернативы. - person Elliott Darfink; 21.05.2020
comment
Спасибо, и это работает для программ субхука с этой командой github.com/Zeex/subhook/issues/45 < / а> - person tobe; 01.06.2020