【问题标题】:Self-modifying code on macOS v10.15 (Catalina) / x64macOS v10.15 (Catalina) / x64 上的自修改代码
【发布时间】:2020-05-27 20:42:28
【问题描述】:

作为移植 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 返回值。我怀疑这是因为苹果设置了一些安全策略,但我没有找到好的文档。

mprotect的源代码在哪里,和/或有什么方法可以同时标记数据区可执行和可写?

我发现编译用

gcc -segprot __DATA rwx rwx

确实将整个数据段标记为 rwx,因此它必须以某种方式可以做正确的事情。但我宁愿只使托管 Forth 单词的区域可执行,而不是整个数据段。

我找到了类似的讨论here,但没有任何解决方案。

【问题讨论】:

  • 通常的方法是在执行更改时将其标记为可写但不可执行,然后在完成后将其标记为只读可执行。
  • 第四个,这似乎不切实际,因为第四个汇编器可以在编译一个新单词的同一段中执行单词。但是玩这个是个好主意,谢谢!
  • 我尝试使用 PROT_READ|PROT_WRITE 调用 mprotect 并获得相同的结果。也许我的系统调用约定是错误的?
  • 好吧,我是个白痴。 1. 我需要and $-PAGE_SIZE,%rdi 而不是and $~PAGE_SIZE,%rdi 2. 我使用gdb 进行跟踪,%rax 中实际返回的是14(EFAULT)。我假设每个手册页只会返回 EACCES、EINVAL 或 ENOTSUP。这有点帮助,但还不够。我可能需要查看 mprotect 的代码才能了解发生了什么。
  • 是否有理由需要静态分配可写/可执行区域?为什么不mmap() 一些具有所需权限的内存?

标签: macos assembly x86-64 macos-catalina forth


【解决方案1】:

我想在其中“取消保护”执行权限的段实际上有两个值来描述其权限:

  1. 初始保护设置,对于 __DATA 我想要 rw-

  2. 最大保护(最松)设置,我想是rwx。

所以首先我需要将 maxprot 字段设置为 rwx。根据 ld 手册页,这应该通过使用标志 -segprot __DATA rwx rw 调用 gccld 来实现。但是,Apple 最近对链接器所做的更改基本上忽略了 maxprot 值,并设置了 maxprot=initprot。

感谢Darfink,您可以在事后使用this script 调整maxprot 位。我认为需要额外的带有特殊权利的代码签名,但它不是,至少对于 __DATA 段。 __TEXT 段可能需要使用com.apple.security.cs.disable-executable-page-protection 权利进行代码签名。

有关更多详细信息,另请参阅here

纵观全局,我还应该指出,与其解除对原本受保护的 __DATA 段的保护,不如为具有 rwx 权限的自修改代码创建一个完整的新数据/代码段开始。这仍然允许操作系统保护其余数据,并且不需要非标准工具。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多