嘿,我在尝试从python调用ioctl linux系统调用时遇到了问题。
在C应用程序中运行以下代码行,我设法获得给定linux名称空间文件描述符的父文件描述符。
#define NS_GET_PARENT _IO(NSIO, 0x2)
struct stat sb;
fd = open("/proc/1337/ns/user", O_RDONLY);
parent_fd = ioctl(fd, NS_GET_PARENT);但是在python中运行同样的脚本会得到“ioctl for device不合适”。
from fcntl import ioctl
NS_GET_PARENT = (0x7b << (4*2)) | 2
f = open('/proc/1337/ns/user')
fd = ioctl(f.fileno(),NS_GET_PARENT)通过在两个脚本上运行strace,我看到这两个脚本都发出了相同的系统调用
open("/proc/1337/ns/user", O_RDONLY) = 3
ioctl(3, _IOC(0, 0xb7, 0x02, 0x00), 0)不同之处在于,C代码实际上将文件描述符返回到父名称空间,而python代码则返回
-1 ENOTTY (Inappropriate ioctl for device)该问题在两台不同的机器(Linux Kernel 4.13.0-37)上复制,并且这两个脚本使用相同的用户运行。有人知道什么会导致这个问题吗?
发布于 2018-06-30 20:42:01
注意:现在有一个Python3库可用,它涵盖了PyPi:linuxns-rel,GitHub:thediveo/linuxns_rel。
这是一个棘手的问题。你的例子失败的原因有两(三)个方面,我花了一些时间来弄清楚它们,并学习了很多关于Linux名称空间的知识。
首先,它是NSIO=0xb7 (和而不是 NSIO=0x7b),因为在Python示例中,NS_GET_PARENT的正确值是(我一开始完全忽略了这个bug,直到我从头开始做了一个有效的PoC,模仿Linux头文件的宏定义非常紧密):
NS_GET_PARENT = (0xb7 << (4*2)) | 2其次,关于Python抛出的异常的信息似乎有第二个错误,这是基于我在使用Python3的实验中看到的:一个不适当的ioctl()是ENOTTY (25)。ENOTTY将适合错误的0x7b而不是0xb7的NSIO。实际上,当我逐字尝试您的示例时,我得到了一个ENOTTY。在这一点上,我没有意识到这是一个无效的请求代码的指示。
使用正确的NS_GET_PARENT请求代码,错误EPERM (1)将适合这样的情况:由于权限限制,Linux内核不允许您查看父名称空间(即使您是超级用户,您可能只在本地的另一个用户用户空间内,等等)。
因此,这段更正后的代码可以工作,但会受到引发OSError EPERMs的访问限制:
from fcntl import ioctl
NS_GET_PARENT = (0xb7 << (4*2)) | 2
with open('/proc/self/ns/user') as f:
fd = ioctl(f.fileno(), NS_GET_PARENT)由于您没有提供有关系统用户名称空间设置的详细信息,因此我怀疑您也感受到了同样的陷阱:向“根”用户名称空间中的进程请求其父用户工作区:这只会抛出一个误导性的EPERM,但就ioctl_ns(2)所说的内容而言,这是正确的。
验证上述代码是否正常工作的一个好技巧是,首先启动Firefox,然后启动sudo lsns -t user。这将显示一组Firefox子进程,它们在自己的用户、ipc和网络名称空间中运行。获取其中一个Firefox子进程的PID,并用它运行上面的示例:它现在应该成功了,如果您fstat()返回的文件描述符,它应该引用根用户名称空间。尝试获取根用户名称空间的父级应该再次失败,并返回EPERM。
https://stackoverflow.com/questions/49539615
复制相似问题