如何通过Python访问Ring 0?

5 投票
5 回答
7645 浏览
提问于 2025-04-16 15:22

这个回答提到,Python中类的命名并不是因为有什么特殊的权限,这里让我感到困惑。

  1. 我该如何在Python中访问更底层的环?
  2. 低级的输入输出(io)是用来访问更底层的环吗?
  3. 如果是的话,我可以通过它访问哪些环?
  4. 这句话 "这个函数是为了低级输入输出而设计的." 是指底层环,还是指其他东西?
  5. C语言在操作系统编程中比较常见。如果Python中有一个OS类,那是不是意味着我可以通过这个类访问C代码?
  6. 假设我在玩一些奇怪的机器语言代码,想要理解它的意思。Python中有没有工具可以用来分析这些东西?如果没有,有没有办法让我用Python控制某个工具来处理这些奇怪的机器语言?[评论中提到的ctypes]
  7. 如果Python和低级特权没有关系,它是否仍然提供一些包装器来控制这些特权?

5 个回答

1

[注意:我错了。在现代Unix系统中,用户模式代码无法再访问环0。-- jc 2019-01-17]

我已经忘记了我对Windows权限了解的那些小知识。在我熟悉的所有Unix系统中,根用户可以访问所有的环0权限。但是我想不出有什么Python模块和权限环之间的对应关系。


也就是说,'os'和'sys'模块并不会给你任何特别的权限。你是否拥有这些权限,完全取决于你的登录凭证。

3

我觉得SimonJ的回答很好,但我想发个自己的,因为从你的评论来看,你似乎还没有完全理解这些内容。

首先,当你启动一个操作系统时,实际上是在把内核加载到内存中,并告诉它“从这个地址开始执行”。内核就是一段代码,基本上就是一个程序,但在启动时没有其他东西被加载,所以如果它想做任何事情,就必须知道如何与它连接的具体硬件进行交互。

其实你不一定要运行一个内核。如果你知道如何控制所有连接的硬件,实际上是可以不需要内核的。然而,早期人们很快意识到,可能会遇到很多种类的硬件,如果系统之间有一个统一的接口来编程,那么代码就可以更方便地移植,也能更快地完成任务。

所以,内核的作用就是控制所有连接到系统的硬件,并提供一个统一的接口,称为API(应用程序编程接口)。在系统上运行的程序代码并不是直接与硬件对话,而是与内核对话。因此,用户程序不需要知道如何让特定的硬盘读取某个特定的扇区,但内核知道。

现在,SimonJ提到的环3的描述就是用户空间的实现方式——通过隔离的、没有特权的进程和虚拟私有地址空间,确保它们之间不会互相干扰,这样可以带来他所描述的好处。

这里还有一个复杂的概念,就是权限。大多数操作系统都有某种形式的访问控制,其中“管理员”对系统有完全的控制,而“用户”则有一些受限的选项。因此,内核请求打开一个属于管理员的文件在这种情况下应该会失败。运行程序的用户可以看作是程序上下文的一部分,程序能做什么受到该用户权限的限制。

大多数你想实现的功能(除非你打算编写内核)都可以在用户空间中以root/管理员用户的身份完成,这时内核不会拒绝任何API请求。它仍然是一个用户空间程序,仍然是环3程序。但对于大多数(几乎所有)用途来说,这已经足够了。作为非root/管理员用户也能完成很多事情。

这同样适用于Python解释器,以及在该解释器上运行的所有Python代码。

让我们来解决一些不确定性:

  • ossys的命名我认为是因为这些是“系统”任务(与urllib2相比)。它们提供了操作和打开文件的方法。例如,这些操作是通过Python解释器进行的,解释器再调用内核。
  • 我不知道有任何内核模式的Python实现。因此据我所知,没有办法用Python编写在内核中运行的代码(Linux/Windows)。
  • 权限有两种类型:一种是硬件访问权限,另一种是内核提供的访问控制系统的权限。Python可以以root/管理员身份运行(实际上在Linux上,许多管理图形工具都是用Python编写的),所以在某种意义上,它可以访问特权代码。
  • 编写C扩展或控制C应用程序与Python的交互,表面上意味着你要么是在使用添加到解释器中的代码(用户空间),要么是在控制另一个用户空间应用程序。但是,如果你用C编写一个内核模块(Linux)或驱动程序(Windows),就可以加载该驱动程序,并通过内核API与之交互。一个例子可能是在C中创建一个/proc条目,然后让你的Python应用程序通过读/写与该/proc条目传递消息(内核模块需要通过读/写处理程序来处理这些消息)。基本上,你编写你想在内核空间运行的代码,并以多种方式添加/扩展内核API,以便你的程序可以与该代码交互。
  • “低级”IO意味着你对发生的IO类型和如何从操作系统获取数据有更多控制。与Python中更高层次的函数相比,它是低级的,这些高层次的函数提供了更简单的读取文件的方法(方便但牺牲了控制)。这就像C语言中read()调用与fread()fscanf()之间的区别。

健康警告:如果你编写内核模块时出错,最好的结果是该模块无法正确加载;最糟糕的情况是你的系统会崩溃/蓝屏,你需要重启。

关于机器指令的最后一点,我在这里无法回答。这是一个完全不同的问题,具体情况也各异。我相信有很多工具可以分析这样的代码,但我不是逆向工程师。不过,我知道许多这样的工具(如gdb、valgrind)并不需要内核模块就能完成它们的工作。

10

Windows和Linux都把内核代码放在环0(ring 0),把用户进程放在环3(ring 3)。这样做的好处是,用户进程之间可以相互隔离,所以即使某个进程崩溃,系统也能继续运行。相对来说,如果环0的代码出错,可能会导致整个机器崩溃。

环0代码之所以这么重要,是因为它可以直接访问硬件。相比之下,当一个用户模式(环3)的进程需要从磁盘读取数据时,它的步骤是:

  1. 进程执行一个特殊指令,告诉CPU它想要进行系统调用
  2. CPU切换到环0,开始执行内核代码
  3. 内核检查这个进程是否被允许进行这个操作
  4. 如果被允许,就执行这个操作
  5. 内核告诉CPU它已经完成了
  6. CPU切换回环3,把控制权交还给进程

属于“特权”用户(比如root或管理员)的进程也在环3中运行,就像其他用户模式代码一样;唯一的不同是第3步的检查总是通过。这是件好事,因为:

  • root拥有的进程崩溃时不会影响整个系统
  • 内核中没有很多用户模式的功能,比如可交换内存和私有地址空间

至于在更低的环中运行Python代码——内核模式是一个非常不同的环境,Python解释器根本不是为这个环境设计的,比如分配内存的过程是完全不同的。

在你提到的另一个问题中,os.open()open()最终都会调用open()这个系统调用,它会检查进程是否被允许打开对应的文件,并执行实际的操作。

撰写回答