C/汇编:如何更改CPU寄存器中的单个位?
我是软件故障注入领域的一名新研究员,目前我的最终目标是编写一段简单的代码,能够更改CPU寄存器中的一个位。我正在考虑用C语言来做这件事(代码中包括一些汇编调用)。考虑到这一点,我在Stack Overflow中找到了这个伟大的线程&关于如何访问32位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
#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