背景:
编写涉及在 python 程序中执行机器代码的概念证明。为了在osx上做到这一点,所以我不得不使用ctypes和libc.dylib以及以下函数调用:
(禁用 SIP 时(
- 用于分配对齐内存的 Valloc
- 用于授予对已分配内存的 WRX 权限的 Mprotect
- 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 是否有额外的安全功能(即使禁用了 SIP(来限制 mprotect 类型的操作?如果是这样,如何绕过它?
更新:
根据注释中建议@DietrichEpp,在ctypes上使用use_errno=True。CDLL 调用生成了错误。它的结果是 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有特殊限制,并且为每个进程设置了最大映射,因此添加更多权限,同一进程的新映射将超过该最大限制(每个进程有多少具有执行/写入权限的映射(。如果我的假设是正确的,我们如何解决这个问题?
Apple 的手册页不再在线,但请参考 mprotect 的 POSIX 手册页:
如果未通过调用 mmap(( 建立映射,则未指定此函数的行为。
看起来 Linux 在这方面更宽容,并允许您在您想要的任何内存上调用 mprotect((,或多或少。Darwin 更严格,如果你想调用 mprotect((,就要求你使用 mmap(( 的内存。这就是为什么从头到尾阅读整个手册页是值得的原因之一。
仔细想想,这是一个合理的要求。valloc(( 给出的内存由分配器管理,以后必须使用 free(( 返回给分配器,从某种意义上说,mprotect(( 绕过分配器的后背并改变内存的工作方式。对于 mmap(( 和 munmap(( 来说,情况并非如此,它们与 mprotect(( 属于同一个系统调用系列。