cpu架构-cpu如何知道RAM中的地址是否包含整数、预定义的cpu指令或任何其他类型的数据



这让我感到困惑的原因是所有地址都包含一个1和0的序列。那么CPU是如何区分00000100(整数)和00000100(CPU指令)的呢?

首先,不同的命令具有不同的值(操作码)。这就是CPU知道该做什么的方式。

最后,问题仍然存在:什么是命令,什么是数据?

现代PC正在使用von Neumann-体系结构(https://en.wikipedia.org/wiki/John_von_Neumann)其中数据和操作码存储在相同的存储器空间中。(有一些体系结构将这两种数据类型分开,例如哈佛体系结构

详细解释所有内容完全超出了stackoverflow的范围,很可能每条帖子的字符数量不够。

用尽可能少的单词回答这个问题(每个真正在这个级别上工作的人都会因为解释中的捷径而杀了我):

  • 存储器中的数据存储在特定地址
  • 每个CPU建议基本上由3个不同的地址组成(不是值,只是地址!):
    • Adress关于该做什么
    • 关于值的Adress
    • Adress about一个附加值

因此,假设应该执行加法,并且内存中有3个可用的Adress,应用程序将存储(在5+7的情况下)(我在指令中使用了"动词")

Adress | Stored Value
1      | ADD
2      | 5
3      | 7

最后,CPU接收指令1 2 3,这意味着ADD 5 7(这些东西是顺序敏感的![Command][v1][v2])。。。现在事情变得越来越复杂。

CPU会将这些值(实际上不是值,只是值的地址)移动到其寄存器中,然后进行处理。要选择的确切寄存器取决于数据类型、数据大小和操作码。

在命令#1 #2 #3的情况下,CPU将首先读取这些存储器地址,然后知道需要ADD 5 7

根据ADD的操作码,CPU将知道:

  • 将地址#2放入r1
  • 将地址#3放入r2
  • 读取存储在r1中存储地址的内存值
  • 读取存储在r2中存储地址的内存值
  • 将两个值相加
  • 将结果写入内存中的某个位置
  • 将结果放入r3的存储地址
  • 将存储在r3中的地址存储到存储在r1中的内存地址中

请注意这是简化的。实际上,CPU需要确切的指令来确定它是处理还是地址。在组装中,这是通过使用来完成的

  • eax(表示存储在寄存器eax中的值)
  • [eax](表示存储在寄存器eax中地址的存储器中的值)

CPU无法对存储在内存中的值进行计算,因此它忙于将值从内存移动到寄存器,从寄存器移动到内存。

即,如果您有

eax = 0x2

和内存

0x2 = 110011

和指令

MOV ebx, [eax]

这意味着:将当前存储在eax中的地址处的值移动到寄存器ebx中。所以最后

ebx = 110011

(这种情况总是发生在CPU进行单个计算的时候!.Memory->Register->Memory)

最后要求苛刻的应用程序可以读取其预定义的存储器地址#2,从而得到地址#2568并且然后知道计算结果存储在地址#2658。读取该Adress将导致值12(5+7)

这只是正在发生的事情的一个很小的例子。有关这方面的更详细介绍,请参阅http://www.cs.virginia.edu/~evans/cs216/guides/x86.html

人们无法真正掌握为简单添加2个值所做的数据移动和计算量。做CPU所做的事情(在纸上)只需要几分钟就可以计算出"5+7",因为没有"5"one_answers"7"-所有东西都隐藏在内存中的地址后面,指向一些位,根据地址0x1的位指示的内容产生不同的值。。。

缩写:CPU不知道那里存储了什么,但指令告诉CPU如何解释它。

让我们举一个简化的例子。

如果CPU被告知添加存储在位置X的一个字(比方说,一个32位整数),它就会获取该地址的内容并添加它

如果程序计数器到达相同的位置,CPU将再次获取该字并将其作为命令执行。

CPU(除了NX位之类的安全性东西)对数据还是代码都是盲目的。

数据不会意外地作为代码执行的唯一方法是仔细组织代码,使其永远不会引用包含数据的位置和用于操作代码的指令。

当程序启动时,处理器会在预定义的位置开始执行程序。用机器语言编写的程序的作者会有意将程序的开头放在那里。从那里开始,该指令将始终设置处理器将执行的下一个位置——这是一条指令。除非代码中有严重的错误,否则组成程序的所有指令都是如此。

指令可以通过两种主要方式设置处理器下一步的位置:跳转/分支,以及不显式指定。如果指令没有明确指定下一步的位置,则CPU默认为当前指令后面的位置。相比之下,跳转和分支有空间专门编码下一条指令的地址。跳跃总是跳到指定的位置。分支检查条件是否为真。如果是,CPU将跳转到编码位置。如果条件为false,它将直接转到分支之后的指令。

此外,机器语言程序不应该将数据写入用于指令的位置,或者在程序的某个未来点,其他指令可能会尝试运行被数据覆盖的内容。发生这种情况可能会导致各种各样的坏事发生。那里的数据可能有一个"操作码",与处理器知道要做的任何事情都不匹配。或者,那里的数据可以告诉计算机做一些完全出乎意料的事情。不管怎样,你都会有糟糕的一天。很高兴你的编译器从来不会搞砸,也不会意外地插入这样的东西。

不幸的是,有时使用编译器的程序员会搞砸,并做一些事情,告诉CPU在分配给数据的区域之外写入数据。(在C/C++中发生这种情况的一种常见方式是将数组分配为长L项,并在写入数据时使用索引>=L。)将数据写入为代码留出的区域是缓冲区溢出漏洞的原因。有些程序可能有一个错误,让远程机器欺骗程序将数据(远程机器发送的)写入到为数据留出的区域的末尾之外,并写入为代码留出的区域。然后,在稍后的某个时刻,处理器执行该"数据"(记住,该数据是从远程计算机发送的)。如果远程计算机/攻击者很聪明,他们会精心制作越过边界的"数据",使其成为进行恶意操作的有效指令。(为了给他们更多的访问权限,可以销毁数据、从内存发回敏感数据等)。

这是因为ISA必须考虑什么是有效的指令集以及如何对数据进行编码:内存地址/寄存器/文字。

有关如何设计ISA的更多一般信息,请参阅此https://en.wikipedia.org/wiki/Instruction_set

简言之,操作系统"告诉"它下一条指令在哪里。在x64的情况下,有一个名为rip(指令指针)的特殊寄存器,它保存要执行的下一个指令的地址。它将自动读取该地址的数据,对其进行解码和执行,并自动将rip增加指令的字节数。

通常,OS可以将存储器(页面)的区域标记为保存或不保存可执行代码。如果错误或利用漏洞试图修改可执行内存,则应该发生错误,类似地,如果CPU发现自己试图执行不可执行内存时,它也会/应该发出错误信号并终止程序。现在你进入了软件病毒的奇妙世界!

最新更新