Самомодифицирующийся код в macOS v10.15 (Catalina) / x64 - PullRequest
1 голос
/ 27 мая 2020

В рамках портирования компилятора Forth я пытаюсь создать двоичный файл, который позволяет самомодифицирующийся код. Подробные сведения о кровавых событиях: https://github.com/klapauciusisgreat/jonesforth-MacOS-x64

В идеале я создаю кучу страниц для пользовательских определений и вызываю mprotect следующим образом:

#define __NR_exit 0x2000001
#define __NR_open 0x2000005
#define __NR_close 0x2000006
#define __NR_read 0x2000003
#define __NR_write 0x2000004
#define __NR_mprotect 0x200004a

#define PROT_READ 0x01
#define PROT_WRITE 0x02
#define PROT_EXEC 0x04
#define PROT_ALL (PROT_READ | PROT_WRITE | PROT_EXEC)
#define PAGE_SIZE 4096


// https://opensource.apple.com/source/xnu/xnu-201/bsd/sys/errno.h
#define EACCES    13              /* Permission denied */
#define EINVAL    22              /* Invalid argument */
#define ENOTSUP   45              /* Operation not supported */


/* Assembler entry point. */
        .text
        .globl start
start:
        // Use mprotect to allow read/write/execute of the .bss section
        mov $__NR_mprotect, %rax                // mprotect
        lea user_defs_start(%rip), %rdi         // Start address
        and $-PAGE_SIZE,%rdi                    // Align at page boundary
        mov $USER_DEFS_SIZE, %rsi               // Length
        mov $PROT_ALL,%rdx
        syscall
        cmp $EINVAL, %rax
        je 1f
        cmp $EACCES,%rax
        je 2f
        test %rax,%rax
        je 4f                                   // All good, proceed:

        // must be ENOTSUP
        mov $2,%rdi                     // First parameter: stderr
        lea errENOTSUP(%rip),%rsi       // Second parameter: error message
        mov $8,%rdx                     // Third parameter: length of string
        mov $__NR_write,%rax            // Write syscall
        syscall
        jmp 3f

1:
        mov $2,%rdi                     // First parameter: stderr
        lea errEINVAL(%rip),%rsi        // Second parameter: error message
        mov $7,%rdx                     // Third parameter: length of string
        mov $__NR_write,%rax            // Write syscall
        syscall
        jmp 3f

2:
        mov $2,%rdi                     // First parameter: stderr
        lea errEACCES(%rip),%rsi        // Second parameter: error message
        mov $7,%rdx                     // Third parameter: length of string
        mov $__NR_write,%rax            // Write syscall
        syscall

3:
        // did't work -- then exit
        xor %rdi,%rdi
        mov $__NR_exit,%rax     // syscall: exit
        syscall

4:
// All good, let's get started for real:

.
.
.

        .set RETURN_STACK_SIZE,8192
        .set BUFFER_SIZE,4096
        .set USER_DEFS_SIZE,65536*2 // 128 kiB ought to be enough for everybody

        .bss
        .balign 8
user_defs_start:
        .space USER_DEFS_SIZE

Однако я получаю возвращаемое значение EACCES. Я подозреваю, что это из-за некоторой политики безопасности, установленной Apple, но я не нахожу хорошей документации.

Где находится исходный код для mprotect и / или какие методы помечают исполняемый файл области данных и с возможностью записи одновременно?

Я обнаружил, что компиляция с

gcc -segprot __DATA rwx rwx

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

Я нашел похожее обсуждение здесь , но без какого-либо решения.

1 Ответ

3 голосов
/ 29 мая 2020

Сегмент, в котором я хочу «снять защиту» с разрешения exe c, на самом деле имеет два значения, описывающих его разрешения:

  1. начальные настройки защиты, которые для __DATA я хочу rw-

  2. максимальная защита (самая слабая) настройки, которые я хочу быть rwx.

Итак, сначала мне нужно установить поле maxprot на rwx . Согласно справочной странице ld, это должно быть достигнуто путем вызова g cc или ld с флагами -segprot __DATA rwx rw. Однако недавнее изменение, внесенное Apple в компоновщик, по существу игнорирует значение maxprot и устанавливает maxprot = initprot.

Благодаря Darfink , вы можете использовать этот сценарий чтобы настроить бит maxprot постфактум. Я думал, что требуется дополнительная подпись кода со специальными правами, но это не так, по крайней мере, для сегмента __DATA. Для сегмента __TEXT может потребоваться подпись кода с правом com.apple.security.cs.disable-executable-page-protection.

Также см. здесь для получения более подробной информации.

Глядя на картинку в более крупном размере, я также должен укажите, что вместо того, чтобы снять защиту с частей сегмента __DATA, защищенного иным образом, может быть лучше с самого начала создать полностью новый сегмент данных / кода только для самомодифицирующегося кода с разрешениями rwx. Это позволяет по-прежнему защищать остальные данные операционной системой и не требует нестандартных инструментов.

...