【问题标题】:Accessing values of process independent registers in c在c中访问与进程无关的寄存器的值
【发布时间】:2017-07-24 07:13:09
【问题描述】:

许多寄存器(如数据寄存器)包含对应于当前正在执行的进程的值。但是,有一些寄存器对于所有进程都是通用的

我想读取一些非进程特定的寄存器,可以从 2 个不同的进程访问,并且仍然能够获得相同的值。

我需要 x86-64 架构的代码。 (在我的桌面上键入时,这 3 个命令 uname -m,-p,-i 返回 x86_64)

谢谢。

【问题讨论】:

  • 好的,您需要处理器的用户手册、操作系统的源代码、汇编程序和一些运气。请注意,即使存在这种机制,通常也不是进程通信的方式。如果三个进程希望通过这种机制进行通信会发生什么?
  • 你能定义哪些寄存器是“控制寄存器”吗?因为 PC 和 SP 对于不同的进程肯定包含不同的值。
  • 这通常使用系统调用来完成,这样代码就可以在不同的处理器之间移植。
  • 请选择 C ​​和 Python 之一。我不会和你玩“那不是我真正想要的编程语言”的游戏。
  • 我已将“64 位架构”更改为“x86-64 架构”。 x86-64 是众多 64 位架构之一,但从您的帖子来看,它是最有可能的一种。好的!现在我可以准备答案了。

标签: c assembly x86 x86-64 cpu-registers


【解决方案1】:

x86-64 架构有很多这样的控制寄存器。如果没有提升的权限,它们中的大多数都无法读取,那些可以被标记的。您可能想阅读this article 以获取有关每个寄存器中位的详细说明。

在 Linux 上,您可以使用iopl 系统调用获得相关的提升权限。 iopl(3) 为您提供所需的所有权限。

  • 标志寄存器包含有关最近算术运算以及一些配置的信息。无需特殊权限即可使用pushf 指令读取。阅读this article了解更多详情。
  • 段寄存器csdsesssfsgs 包含段选择器。在现代操作系统上,这些通常对所有进程都是固定的,并且可以使用 mov r16,segr 读取而无需提升权限。
  • cr0 寄存器包含与内存保护相关的配置。任何进程都可以使用 rmsw 指令读取其低 16 位,其余位可以使用 mov r32,cr0 以提升的权限读取,所有其他控制寄存器也可以。
  • cr2 寄存器包含最后一个页面错误的地址。
  • cr3 寄存器包含页目录的地址。
  • cr4 寄存器包含额外的 CPU 配置。
  • cr8 寄存器包含有关任务优先级的信息

还有一堆特定于模型的寄存器,可以使用rmsr 指令读取。

要读取这些寄存器,请使用内联汇编。这是前面提到的所有寄存器的内联汇编。要阅读rflags 寄存器,还请查看this question 以了解一些注意事项。

/* read rflags */
uint64_t rflags;
asm("pushf; popf %0" : "=rm"(rflags));

/* read segment register, replace sr with the segment you want */
uint16_t seg;
asm("mov %sr,%0" : "=rm"(seg));

/* read low bits of cr0 */
/* on some CPUs, only the low 16 bits are correct,
/* on others all 32 bit are correct */
uint32_t cr0;
asm("smsw %0" : "=r"(cr0));

/* everything below here requires elevated privileges */

/* read control register, replace cr with register name */
uint64_t cr;
asm("mov %cr,%0" : "=rm"(cr));

/* read model specific register. msr contains register number */
uint32_t msr_no = 0xC0000080, msr_hi, msr_lo;
asm("rdmsr" : "=a"(msr_lo), "=d"(msr_hi) : "c"(msr_no));
uint64_t msr_val = (uint64_)msr_hi << 32 | msr_lo;

【讨论】:

  • 大多数操作系统使用fsgs 进行线程本地存储,因此无论他们使用哪一个都具有每个线程的值。此外,在一个进程中更改段寄存器不会影响其他进程。每个进程都有它的段 regs 或者在上下文切换时保存/恢复,或者只是设置为操作系统认为它们应该是的。 (实际上,您可以在 Linux 或 Windows 下跳到不同的 cs 值,以在以 64 位模式启动的进程中运行 32 位代码。但是在中断之后,操作系统可能会返回到您的进程在它认为应该处于的模式下,而不是保存/恢复 CS)
  • @PeterCordes 在典型用法中,您从不接触任何段寄存器,操作系统对所有进程使用相同的段。要交换 TLS 段,操作系统只需编辑 ldt 并重新加载段选择器。或者,在 amd64 上,使用该特殊指令重新加载 gs 段基础。
  • 是的,当然你通常不会惹他们。但是由于用户空间可以弄乱它们,因此内核假设它没有,这将是一个安全问题。因此,它们必须写在每个用户->内核开关上。但是,不一定在 kernel->user 上,因为确实所有进程通常都会获得相同的值(CS 除外,其中 32 位进程获得不同的选择器)。
  • 我不敢相信我开始谈论一个 SO 问题中的片段,而这不是那些糟糕的 16 位问题之一。 :P 我真的很高兴这些天您需要了解它们以优化用户空间代码的数量目前为零(TLS 除外)。
猜你喜欢
  • 1970-01-01
  • 2018-07-24
  • 2012-06-15
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 2012-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多