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

C/汇编:如何更改CPU寄存器中的单个位?,c,gcc,assembly,inline-assembly,cpu-registers,C,Gcc,Assembly,Inline Assembly,Cpu Registers,我是软件故障注入领域的一名新研究员,目前我的最终目标是编写一段简单的代码,能够更改CPU寄存器中的一个位。我正在考虑用C语言来做这件事(代码中包括一些汇编调用)。考虑到这一点,我在Stack Overflow中找到了这个伟大的线程&关于如何访问32位CPU寄存器内容的简单示例:通过这种方式,我能够编写以下简单代码: #include <stdio.h> int main() { register int value; register int ecx asm("e

我是软件故障注入领域的一名新研究员,目前我的最终目标是编写一段简单的代码,能够更改CPU寄存器中的一个位。我正在考虑用C语言来做这件事(代码中包括一些汇编调用)。考虑到这一点,我在Stack Overflow中找到了这个伟大的线程&关于如何访问32位CPU寄存器内容的简单示例:通过这种方式,我能够编写以下简单代码:

#include <stdio.h>

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;
}
#包括
int main()
{
寄存器int值;
注册int ecx asm(“ecx”);
printf(“ecx的内容:%d\n”,ecx);
asm(“movl%%ecx,%0;”:“=r”(值):;//程序集:将ecx值存储到变量值中
printf(“值的内容:%d\n”,值);
返回0;
}
这似乎是对这个主题的一个很好的介绍,这里提供的答案给了我很好的洞察力和信息来源(我已经在阅读GCC文档),但现在我需要进一步,我。e、 ,我需要了解如何更改CPU寄存器中单个位的内容(或者至少,首先,更简单一点:如何更改CPU寄存器值?)。如果有人能给我一个提示或告诉我最合适的来源来寻找它,我将不胜感激

祝您一切顺利&提前感谢, 若昂


注:不知道这是否有帮助,但我正在使用CentOS 6.5 32位系统(尽管CPU是64位的,更准确地说是Intel Pentium双CPU E2180@2.00 GHz)。另外,我以前接触过汇编语言,但就像10年前一样,在一个课程单元上学习了几个月,所以现在我正试图复习一下我对汇编语言的一点知识。

在汇编中修改寄存器位子集的通常方法是对常量使用逻辑运算

和%eax,0xFFFFFFFE
取消设置第0位

或%eax,0x01
设置第0位

XOR%eax,0x01
翻转第0位

(感谢@harold的更正)

话虽如此,正如评论中所指出的,您可能不想在程序中直接使用内联汇编来模拟硬件故障。它将与您引入修改的地方紧密绑定,在源代码和二进制级别混合引入错误不是我推荐的方法(您的编译器可以并且将重新组织和优化代码,因此很可能最终导致意外行为。请参见.Balakrishnan和Reps的“您看到的不是您执行的”)

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

编辑: 由于OP要求在评论中提供一个内联asm的“hello world”示例,因此如下所示:

#include <stdlib.h>
#include <stdio.h>

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

gcc test.c -S
它将创建test.s,一个包含程序的程序集输出的文件。该文件将让您了解有关代码的许多内容,并且您应该在使用内联asm编译之前生成程序集(至少当您不了解某些内容时)

然后,您可以使用以下命令从程序集中组装二进制文件:

gcc test.s -o test
关于x86,有几点需要注意:

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

在汇编程序中修改寄存器位子集的通常方法是对常量使用逻辑运算

和%eax,0xFFFFFFFE
取消设置第0位

或%eax,0x01
设置第0位

XOR%eax,0x01
翻转第0位

(感谢@harold的更正)

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

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

编辑: 由于OP要求在评论中提供一个内联asm的“hello world”示例,因此如下所示:

#include <stdlib.h>
#include <stdio.h>

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

gcc test.c -S
它将创建test.s,一个包含程序的程序集输出的文件。该文件将让您了解有关代码的许多内容,并且您应该在使用内联asm编译之前生成程序集(至少当您不了解某些内容时)

然后,您可以使用以下命令从程序集中组装二进制文件:

gcc test.s -o test
关于x86,有几点需要注意:

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

    • 基于英特尔的处理器提供了一次操作一位的指令。这些指令通常被忽略,并被
      异或
      测试
      指令所取代,这些指令可以同时操作多个位。而且,较旧的处理器执行这些指令的速度非常慢。我不这么认为知道他们是否仍然很慢,也许不是

      相应的组装说明为(AT&T语法)

      测试位(CF=位值):

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

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

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