背景:
编写涉及在 python 程序中执行机器代码的概念证明。要在 osx 上执行此操作,我必须使用 ctypes 和 libc.dylib 以及以下函数调用:
(禁用 SIP)
- valloc 分配对齐的内存
- mprotect 授予 wrx 分配内存的权限
- 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 现在将失败并显示段错误错误消息(b/c 写入可能只有读取权限的内存空间),并且程序陷入困境......
问题出在 mprotect 上,这种方法在 linux 中效果很好,我现在看到 mac osx 的结果非常不同
问题:
Mac OSX 是否具有限制 mprotect 操作类型的额外安全功能(即使禁用 SIP)?如果是这样,如何绕过它?
更新:
根据评论中建议的@DietrichEpp,在 ctypes.CDLL 调用上使用 use_errno=True 会生成 errno。它评估为 errno: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 权限的映射)。如果我的假设是正确的,我们该如何解决呢?