首先,观察:没有什么说您必须将mmap()
机器指令放入内存或将它们保存回文件。read()
并且write()
也可以这样做,只需注意您应该为此目的创建一个可写且可执行的私有映射。
显然,如果要在同一进程中执行,您不能可靠地禁用对将调用您将加载的可执行代码的堆栈区域的写入,因为这将使堆栈不可用。您可以通过注释变量或使用程序集来解决此问题。
您的下一个选择是fork()
. 您可以exec
将孩子放入一个特殊的包装器可执行文件中,该可执行文件允许恶意可执行代码的破坏和自省最小化(提供简单的加载/转储),或者您可以通过让孩子修改自身以达到相同的效果来做同样的事情。这仍然不是 100% 安全的。
提案0
- 创建与最小库 ( ) 链接的独立二进制文件
-nodefaultlibs
。
- 之后
fork
,ptrace(PTRACE_TRACEME)
在孩子中(以便您可以可靠地读取内存内容并进行其他干预),并关闭除管道之外的所有句柄(只是stdin
为了简单起见)。exec()
进入上述包装二进制文件。
在包装二进制文件中:
mmap
具有写入和执行权限的已知位置的私有区域。或者,如果大小是固定的,您可以静态分配该区域。
- 将管道的内容读入该区域。
- 关闭管道。现在该进程没有打开的句柄。
prctl(PR_SET_SECCOMP, 1)
. 现在唯一有效的系统调用是_exit
and sigreturn
。由于该过程不能raise
,sigreturn
应该没有任何有用的效果。
- 从主堆栈中删除写权限(应该是唯一的堆栈)。由于您无意返回,并且会立即跳转,因此您不需要再次触摸堆栈。
- 跳转到区域内的起始位置。使用程序集执行此操作,或创建一个函数指针并调用它(如果您可以在不推入堆栈的情况下使其工作)。现在您应该正在执行一个内存区域,该区域是唯一可用的可写区域。主堆栈受到保护,由于缺乏库支持,堆不应该被使用。
在父级中:
- 使用
ptrace
or wait
,捕获错误或成功的完成。
/proc/<pid>/mem
通过或等效于文件读取已知位置的映射区域。