10.13 High Sierra OSX - Python mprotect 在使用 ENOMEM 授予执行权限时总是失败
10.13 High Sierra OSX - Python mprotect always fails when granting exec permission, with ENOMEM
背景:
编写涉及在 python 程序中执行 mac 内部代码的概念证明。要在 osx 上执行此操作,所以我必须使用 ctypes 和 libc.dylib 以及以下函数调用:
(禁用 SIP)
- valloc 分配对齐内存
- mprotect 授予 wrx 分配内存的权限
- memmove 将可执行代码复制到分配的内存;投掷;和
执行...
问题:
问题出现在 mprotect 函数调用中,它总是 return -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 现在将失败并出现段错误消息(b/c 正在写入可能只有读取权限的内存 space),并且程序出现故障...
问题出在 mprotect 上,此方法在 linux 中非常有效,我现在看到 mac osx
的结果非常不同
问题:
Mac OSX 是否有限制 mprotect 操作类型的额外安全功能(即使禁用 SIP)?如果是这样,如何绕过它?
更新:
根据@DietrichEpp 在评论中的建议,在 ctypes.CDLL 调用上使用 use_errno=True 生成了错误号。它评估为错误号:12,无法分配内存。此 errno 是 mprotect 手册页上 ENOMEM 的值。
尽管手册页上有一些 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 每个进程的特权)。如果我的假设是正确的,我们该如何解决?
Apple 的手册页不再在线,但请参阅 POSIX man page for mprotect:
The behavior of this function is unspecified if the mapping was not established by a call to mmap().
似乎 Linux 在这方面更宽容,并允许您或多或少地在您想要的任何内存上调用 mprotect()。 Darwin 更严格,如果您想调用 mprotect(),则要求您使用 mmap() 中的内存。这就是从头到尾阅读整个手册页值得的原因之一。
仔细想想,这是一个合理的要求。 valloc() 提供的内存由分配器管理,稍后必须通过 free() 返回给分配器,而 mprotect() 在某种意义上绕过分配器并改变内存的工作方式。 mmap() 和 munmap() 不是这种情况,它们与 mprotect() 属于同一系列系统调用。
背景:
编写涉及在 python 程序中执行 mac 内部代码的概念证明。要在 osx 上执行此操作,所以我必须使用 ctypes 和 libc.dylib 以及以下函数调用:
(禁用 SIP)
- valloc 分配对齐内存
- mprotect 授予 wrx 分配内存的权限
- memmove 将可执行代码复制到分配的内存;投掷;和 执行...
问题:
问题出现在 mprotect 函数调用中,它总是 return -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 现在将失败并出现段错误消息(b/c 正在写入可能只有读取权限的内存 space),并且程序出现故障...
问题出在 mprotect 上,此方法在 linux 中非常有效,我现在看到 mac osx
的结果非常不同问题:
Mac OSX 是否有限制 mprotect 操作类型的额外安全功能(即使禁用 SIP)?如果是这样,如何绕过它?
更新:
根据@DietrichEpp 在评论中的建议,在 ctypes.CDLL 调用上使用 use_errno=True 生成了错误号。它评估为错误号:12,无法分配内存。此 errno 是 mprotect 手册页上 ENOMEM 的值。
尽管手册页上有一些 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 每个进程的特权)。如果我的假设是正确的,我们该如何解决?
Apple 的手册页不再在线,但请参阅 POSIX man page for mprotect:
The behavior of this function is unspecified if the mapping was not established by a call to mmap().
似乎 Linux 在这方面更宽容,并允许您或多或少地在您想要的任何内存上调用 mprotect()。 Darwin 更严格,如果您想调用 mprotect(),则要求您使用 mmap() 中的内存。这就是从头到尾阅读整个手册页值得的原因之一。
仔细想想,这是一个合理的要求。 valloc() 提供的内存由分配器管理,稍后必须通过 free() 返回给分配器,而 mprotect() 在某种意义上绕过分配器并改变内存的工作方式。 mmap() 和 munmap() 不是这种情况,它们与 mprotect() 属于同一系列系统调用。