C /汇编:如何更改CPU寄存器中的单个位?

我是软件故障注入领域的新研究员,目前我的最终目标是编写一段能够改变CPU寄存器中单个位的代码。 我在考虑用C语言(在代码中包含一些程序集调用)。 考虑到这一点,我在Stack Overflow中找到了这个很棒的线程和关于如何访问32位CPU寄存器内容的简单示例: 是否可以在C中访问32位寄存器? 这样,我就能编写这个简单的代码:

#include  int main() { register int value; register int ecx asm("ecx"); printf("Contents of ecx: %d\n", ecx); asm("movl %%ecx, %0;" : "=r" (value) : ); //Assembly: this stores the ecx value into the variable value printf("Contents of value: %d\n", value); return 0; } 

这似乎是对这个主题的一个很好的介绍,那里提供的答案给了我很好的见解和信息来源(我已经阅读了GCC文档),但现在我需要进一步,即我需要了解怎么能我更改了CPU寄存器中单个位的内容(或者至少,要启动,更简单一些:如何更改CPU寄存器值?)。 如果有人能给我一个提示或告诉我最合适的来源,我会非常感激。

最好的,并提前感谢,João

PS:不知道这是否有帮助,但我正在使用CentOS 6.5 32位系统(虽然CPU是64位,更准确地说是Intel Pentium双CPU E2180 @ 2.00 GHz)。 此外,我之前曾与大会有过接触,但就像10年前一样,在单个课程单元上工作了几个月,所以目前我正在尝试回顾一下我对该语言的一点知识。

修改程序集中寄存器位子集的常用方法是使用带常量的逻辑运算。

AND %eax, 0xFFFFFFFE取消设置第0位。

OR %eax, 0x01设置第0位。

XOR %eax, 0x01翻转第0位。

(感谢@harold的更正)。

话虽如此,并且如评论中所述,您可能不希望在程序中直接使用内联汇编来模拟硬件故障。 它将与您引入修改的位置紧密绑定,并且在源和二进制级别混合故障引入不是我建议的方法(您的编译器可以并将重新组织和优化代码,因此很可能是它最终会导致意想不到的行为。请参阅.Balakrishnan和Reps’你所看到的不是你所执行的行为)。

您可以使用二进制检测平台,例如Intel的PIN或现有的故障注入框架。

编辑:由于OP在评论中要求内联asm的“hello world”示例,这里是:

 #include  #include  int main() { register int eax asm("%eax"); asm("xorl %eax, %eax"); asm("xorl $1, %eax"); printf("Content of eax: %d\n", eax); return 0; } 

保存为test.c然后编译:

 gcc test.c -o test 

或更好:

 gcc test.c -S 

这将创建test.s,一个包含程序的程序集输出的文件。 此文件将让​​您了解有关代码的许多内容,并且您应该在使用内联asm进行编译之前生成程序集(至少在您不理解的情况下)。

然后,您可以从程序集中汇编二进制文件:

 gcc test.s -o test 

以下是x86上需要注意的几点:

  • 在实际指令中,我将目标寄存器放在指令的末尾。
  • 立即以“$”为前缀,以“%”注册
  • 操作扩展寄存器的指令(32位寄存器,前缀为“e”)后缀为“l”

基于Intel的处理器提供一次操作一位的指令。 这些指令通常被忽略,并由可以一次操作多个位的ANDORXORTEST指令代替。 旧处理器执行起来也会慢。 我不知道他们是否仍然很慢,可能不是。

相应的汇编指令是(AT&T语法)

测试一下(CF =位值):

 BT imm8, reg BT reg, reg BT reg, mem 

测试和补码(CF =旧位值;新位值=〜旧位值):

 BTC imm8, reg BTC reg, reg BTC reg, mem 

测试和复位(CF =旧位值;新位值= 0)

 BTR imm8, reg BTR reg, reg BTR reg, mem 

测试和设置(CF =旧位值;新位值= 1)

 BTS imm8, reg BTS reg, reg BTS reg, mem 

当用于内存时,它可以primefaces使用(在执行读/修改/写入循环时只有一个CPU访问该内存。)因此,它可用于创建锁和信号量。