【问题标题】:Can modern computers make distinctions between data and instructions at the machine level? [duplicate]现代计算机能否在机器级别区分数据和指令? [复制]
【发布时间】:2021-12-30 17:06:57
【问题描述】:

我正在阅读这本书,名为 The Shellcoder's Handbook,然后我发现了以下信息:

现代计算机并没有真正区分指令和数据。如果 当处理器应该看到数据时,它可以得到指令,它会很高兴 继续执行传递的指令。这一特点使系统 可以利用。

那时我才意识到我们的现代系统是多么脆弱:( 但这本书有点老了,所以我做了一些研究,但找不到足够的信息让我满意。是否还没有任何方法可以区分Data和Instructions?

附带问题 典型的程序集文件包含三个段: .text .bss 和.data

还有其他方法可以区分这些吗? [无元数据]

【问题讨论】:

  • 不可能,比特就是比特。火车轮子能分辨出下一段轨道是钢制的还是其他的?如果它甚至存在?当然不是,它不会知道它是否排成一行并保持到轮子接触它的那一刻。与处理器相同,在将位加载到解码器并尝试解码之前,不可能从指令中区分数据。一个有缺陷的程序实际上可能在处理器前面有数据,它会尽最大努力将这些位作为指令使用,因为无法分辨。处理器非常非常非常愚蠢....
  • 从数据中正确确定指令的唯一方法是首先假设程序员和工具已经构建了符合体系结构及其规则的适当程序。其次,您必须从已知入口点开始,然后按执行顺序解码。因为某些指令路径可能来自用户输入等,您可能无法遵循所有这些路径,并且不能简单地假设您仅通过检查就知道跳转表的全部范围等。在过去甚至今天,有些树枝是伪造的,以甩掉拆卸,造成混乱。
  • 一些二进制文件和文件格式将允许函数标签,允许用户/工具有更多的代码入口点。通过删除标签并将一两个字节扔到反汇编程序的路径中来混淆反汇编程序甚至是 gnu objdump 是微不足道的。
  • 现代计算机与它无关,这就是逻辑的工作方式。你喂它一些东西,希望你在制作正确的东西方面做得对,就像你铺设一些火车轨道并希望你做对了,它不会撞到火车。
  • 问题引用似乎是倒置的:你的意思是“如果一个处理器可以在它应该看到 instructions 时被馈送 data 它会很高兴地继续执行传递的数据 [作为指令]" ?

标签: assembly stack-overflow cpu-architecture microprocessors penetration-testing


【解决方案1】:

简短的回答是否定的,除非您告诉处理器它正在接收什么,否则它不知道其中的区别。如果执行跳转到那里(并且页表允许 exec 权限),那就是代码。如果加载/存储指令将其作为数据访问,则它是数据。 (在使用 JIT 编译将一些机器代码写入缓冲区然后跳转到那里运行它的程序中,两者都可能发生在相同的内存地址上。)

数据位和指令位的一部分可能是相同的,如果没有完整的上下文,就无法区分。

您可以将其推断到极端; 01 是某些数据的一部分还是指令的一部分?

【讨论】:

    【解决方案2】:

    指令是数据。在程序运行之前,构建工具和操作系统将程序视为数据,例如作为数据文件。为了将程序加载到内存中,操作系统将程序的各个部分视为数据,包括.text部分(那些包含程序代码/机器代码指令的部分)。

    对于 8 位数据字节,所有可能的位模式(所有可能的值)都用于传达含义——例如它们区分 256 个不同的 ascii 符号,或表示 0..255 或 -128..127 的数字,或者表示某些较大字编码的部分。由于指令是数据,因此处理器或其他任何人没有固有的方法来区分它们。

    话虽如此,但现代操作系统和处理器允许保护部分,例如 .text,以防止写入。

    还有其他方法可以区分这些吗? [无元数据]

    不,元数据就是方法。

    如果处理器可以在应该看到数据时接收指令,它会很高兴地继续执行传递的指令。

    这是繁琐的措辞。我不知道说“它应该看到数据”是什么意思。我可能会说当程序被愚弄执行程序作为数据的内容时。


    但现代处理器和操作系统的技术可以阻止这一点。将运行时/调用堆栈标记为不可执行就是这样一种方法。这个想法是使用硬件功能将一些程序元数据携带到运行时,这些功能可以有效地标记一些具有执行权限(或不具有)的内存区域。标记区域或页面比标记单个机器代码指令或单个数据字节更有效。

    在此处查看更多信息:https://en.wikipedia.org/wiki/Buffer_overflow_protection

    【讨论】:

      【解决方案3】:

      代码和数据可以通过页表中的执行位在机器级别进行区分。决定因素不能是任何位模式,而是(虚拟)地址。

      从统计上讲,可以合理地确定字节流是否可以解释为有意义的代码。代码包含一个结构,例如数据依赖流,其中任何任意或重复的数据都没有;随机数据可以解释为mov eax, ebx,但典型的代码不会包含很多mov eax, ebx; mov eax, ecx; mov eax, edx,这将是一个完全有效的数据序列。

      【讨论】:

      • 可执行页面中可以有数据,例如使用-zexecstack 编译的程序(或隐式启用,因为您使用了指向 GNU C 嵌套函数的函数指针)。在 ARM 中,只读的“文字池”数据在函数之间移动是正常的,因为 ARM 具有相对较短范围的 PC 相关加载指令,并且工具链演变为以这种方式利用它。因此,页表中的 EXEC 位并不能保证页面中的字节是代码,尽管缺少这样的位会排除它们在此映射中作为代码执行。
      猜你喜欢
      • 2017-01-26
      • 1970-01-01
      • 1970-01-01
      • 2012-06-10
      • 1970-01-01
      • 2011-07-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多