可怕!CPU暗藏了这些未公开的指令!

3408b605bbe302d798a5e1c7b35d267d.jpeg

我们平时编程写的高级语言,都是经过编译器编译以后,变成了CPU可以执行的机器指令:

4e856837f673c59b579fe0168611c530.png

而CPU能支持的指令,都在它的指令集里面了。

很久以来,我都在思考一个问题:

CPU有没有未公开的指令?

或者说:

CPU有没有隐藏的指令?

为什么会有这个问题?

平常我们谈论网络安全问题的时候,大多数时候都是在软件层面。谈应用程序的漏洞、后端服务的漏洞、第三方开源组件的漏洞乃至操作系统的漏洞。

但很少有机会去触及硬件,前几年爆发的熔断幽灵系列漏洞,就告诉我们,CPU也不是可信任的。

要是CPU隐藏有某些不为人知的指令,这是一件非常可怕的事情。

如果某一天,某些国家或者某些团体组织出于某种需要,利用这些隐藏的指令来发动攻击,后果不堪设想。

虽然想到过这个问题,但我一直没有付诸实践去认真的研究。

直到前段时间,极客时间的一位老师分享了一份PDF给我,解答了我的疑惑。

这份PDF内容是2017年顶级黑客大会Black Hat上的一篇报告:《us-17-Domas-Breaking-The-x86-ISA》,作者是大神:@xoreaxeaxeax,熟悉汇编的同学知道这名字是什么意思吗?

43e082b5879f8a3d98de8ae98cd02ba0.png

这份PDF深度研究了x86架构CPU中隐藏的指令,原报告因为是英文,看起来有些晦涩,这篇文章,我尝试用大家易懂的语言来给大家分享一下这篇非常有意思的干货。

有些人会问:真的会有隐藏指令的存在吗,CPU的指令集不是都写在指令手册里了吗?

我们以单字节指令为例,单字节的范围是0x00-0XFF,总共256种组合,Intel的指令手册中是这样介绍单字节指令的:

e8a0655a522577e39ed1fd09ec86b28d.png

横向为单字节的高四位,纵向为单字节的低四位,顺着表格定位,可以找到每一个单字节指令的定义。比如我们常见的nop指令的机器码是0x90,就是行为9,列为0的那一格。

但是不知道你发现没有,这张表格中还有些单元格是空的,比如0xF1,那CPU拿到一个为0xF1的指令,会怎么执行呢?

指令手册没告诉你。

这篇报告的主要内容就是告诉你,如何去寻找这些隐藏的指令。

指令集的搜索空间

想要找到隐藏的指令,得先明确一个问题:一条指令到底有多长,换句话说,有几个字节,我们应该在什么样的一个范围内去寻找隐藏指令。

如果指令长度是固定的,比如JVM那样的虚拟机,那问题好办,直接遍历就行了。

但问题难就难在,x86架构CPU的指令集属于复杂指令集CISC,它的指令不是固定长度的。

有单字节指令,比如:

90  nop

CC  int 3

C3  ret

也有双字节指令,比如:

8B C8  mov ecx,eax

6A 20  push 20h

还有三四节、四字节、五字节···最长能有十几个字节,比如这条指令:

指令:lock add qword cs:[eax + 4 * eax + 07e06df23h], 0efcdab89h

机器码:2e 67 f0 48 818480 23df067e 89abcdef

一个字节、两个字节,甚至三个四个遍历都还能接受,4个字节最多也就42亿多种组合,对于计算机来说,也还能接受。

但越往后,容量是呈指数型增长,这种情况再去遍历,显然是不现实的。

指令搜索算法

这份报告中提出了一种深度优先的搜索算法:

dd4546cf651a2b15bfbeaa5fc0beabc2.png

该算法的指导思想在于:快速跳过指令中无关紧要的字节

67efff647c60da7ed7fe1211e6967f8b.png

怎么理解这句话?

比如压栈的指令push,下面几条虽然字节序列不同,但变化的只是数据,其实都是压栈指令,对于这类指令,就没必要花费时间去遍历:

  • 68 6F 72 6C 64    push 646C726Fh

  • 68 6F 2C 20 77    push 77202C6Fh

  • 68 68 65 6C 6C    push 6C6C6568h

第一个字节68就是关键字节,后面的四个字节都是压入栈中的数据,就属于无关紧要的字节。

如果能识别出这类,快速跳过,将能够大面积减少需要遍历的搜索空间。

(PS:本文来自公众号:编程技术宇宙)

上面只是一个例子,如何能够系统化的过滤掉这类指令呢?报告中提出了一个方案:

5a1f4f285bba849a4554458396d0e73d.png

观察指令中的有意义的字节,它们对指令的长度和异常表现会产生冲击。

又该怎么理解这句话?

还是上面那个例子,当尝试修改第一个字节68的时候,这一段二进制序列可能就完全变成了别的指令,甚至指令长度都会发生变化(比如把68改成90,那就变成了一个字节的nop指令),那么就认为这第一个字节是一个有意义的字节,修改了它会对指令的长度产生重要影响。

反之,如果修改后面字节的数据,会发现这仍然是一条5个字节的压栈指令,长度没变化,也没有其他异常行为表现与之前不同,那么就认为后面几个字节是无关紧要的字节。

在这个指导思想下,我们来看一个例子:

从下面这一段数据开始出发:

5eeed779d0b9f91165e680b1d3e1034d.png

我们从两个字节的指令开始遍历:

72a7977d809767bdeb6fcec24f2f7e39.png

把最后那个字节的内容+1,尝试去执行它:

5e1895bc8fdcd0c83db68be18725e2f3.png

发现指令长度没有变化(具体怎么判断指令长度变没变,下一节会重点讨论),那就继续+1,再次尝试执行它:

c062f3c073041a364ec069e6302c7902.png

一直这样加下去,直到发现加到4的时候,指令长度发生了变化,长度超过了2(但具体是多少还不知道,后文会解释):

7ebeb923685e5d50142615d34ede9200.png

那么在这个基础上,长度增加1位,以指令长度为3的指令来继续上面的探索过程:从最后一位开始+1做起。

b214c18e72ab5ec8f91b622513f29979.png

随着分析的深入,梳理一下指令搜索的路径图:

54b0e7b720f07f4c17c504c7e561763e.png

当某一条的最后一个字节遍历至FF时,开始往回走(就像递归,不能一直往下,总有回去的时候):

229d02672615f3af551cc5fb9f8e7445.png

往回走一个字节,将其+1,继续再来:

3a9c2070ad8b77d56345dcaf68f1332c.png

按照这个思路,整个要搜索的指令空间压缩到可以接受遍历的程度:

b3f8b170f4b1c8919596dc1f63943a2e.png

如何判定指令长度

现在来解答前面遗留的一个问题。

上面这个算法能够工作的一个重要前提是:

我们得知道,给末尾字节+1后,有没有影响指令的长度。

要判断某个字节是不是关键字节,就得知道这个字节的内容变化,会不会影响到指令长度,所以如果无法判断长度有没有变化,那上面的算法就无从谈起了。

所以如何知道长度有没有变化呢?报告中用到了一个非常巧妙的方法。

假设我们要评估下面这一串数据,前面开头到底多少个字节是一条完整指令。

cb24c03257d6655efbff742930195fe5.png

可能第一个字节0F就是一条指令。

也可能前面两个字节0F 6A是一条指令。

还可能前面五个字节0F 6A 60 6A 79 6D是一条指令。

到底是什么情况,我们不知道,让我们用程序来尝试推导出来。

准备两个连续的内存页面,前面一个拥有可执行的权限,后面一个不能执行。

880e38e031185d01a980888e6498794f.png

记住:当CPU发现指令位于不可执行的页面中时,它会抛异常!

现在,在内存中这样放置上面的数据流:第一个字节放在第一个页面的末尾位置,后面在字节放在第二个不可执行的页面上。

b9c42848eb4a6aa49773d0761bb041f5.png

然后JMP到这条指令的地址,尝试去执行它,CPU中的译码器开始译码:

13a620d400e41419402cc7d458228f84.png

译码器译码发现是0F,不是单字节指令,还需要继续分析后面的字节,继续取第二个字节:

90988fde530dbb5bd24c9930accd17ec.png

但注意,第二个字节是位于不可执行的页面,CPU检查发现后会抛出页错误异常:

fac6be6ed4a59800187b6c509e67a857.png

如果我们发现CPU抛了异常,并且异常的地址指向了第二个页面的地址,那么我们可以断定:这条指令的长度肯定不止一个字节。

a69790b7d3a9a3ef59bfdfc6226aa67e.png

既然不止一个字节,那就往前挪一下,放两个字节在可执行页面,从第三个字节开始放在不可执行页面,继续这个过程。

8e2f2c3c68fa41f50cc624bf4636b635.png

继续上面这个过程,放三个字节在可执行页面:

27de4e689767d1c01fce8bb980578657.png

四个:

c7e560bef9fb5d430bf0f9db52bd3907.png

当放了四个字节在可执行页面之后,事情发生了变化:

指令可以执行了!虽然也抛了异常(因为天知道这是个什么指令,会抛什么异常),但页错误的地址不再是第二个页面的地址了!

1297bc803acfb999afb897a94250f431.png

有了这个信号,我们就知道,前面4个字节是一条完整的指令:

8a0002d4fff4d99c1f4161f0ea067647.png

挖掘隐藏指令

现在核心算法和判断指令长度的方法都介绍完了,可以正式来开挖,挖出那些隐藏的指令了!

以一台Intel Core i7的CPU为目标,来挖一挖:

32ad1448394a70dad5d51401cdebf459.png

挖掘成果,收获颇丰:

8c5cd56ee545aaa19cde8889e245147a.png29389051def56a61f0d02efa329af6cf.png

这些都是Intel指令集手册中未交待,但CPU却能执行的指令。

然后是AMD Athon的CPU:

f776f5da0fb15858a65157045ea7ec49.png

挖掘成果:

5aaf7702003fbd14fda817eb2da29ac1.png

那么,这些隐藏的指令是做什么的呢?

有些已经被逆向工程分析了;还有的就是毫无记录,只有Intel/AMD自己人知道了,谁知道它们用这些指令是来干嘛的?

软件即便是开源都能爆出各种各样的问题,何况是黑盒一样的硬件。

CPU作为计算机中的基石,它要是出了问题,那可是大问题。

我不是阴谋论,害人之心不可有,但防人之心不可无。

看完这些,我对国产、安全、自主可控这几个字的理解又加深了一层。

最后,大家对这些隐藏指令有何看法?欢迎评论区分享你的观点。

e22a72959dadb15fffac7a187756bda9.png

END

作者:轩辕之风O

来源:编程技术宇宙


版权归原作者所有,如有侵权,请联系删除。

d7e912b0fa4ccd903fe8d0e17e5ea530.jpeg

0737dc9c04d4faf343182e13dc7e13f5.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463732.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

A star算法优化二

本文目的是对A*寻路算法所生成的路径进行一些人性化的调整,使其看起来不至于太机械化。关于A*算法的原理与实现,读者可以阅读其他资料,这里不再详细阐述。 如何写估价函数A*寻路算法本质上是一个有方向性的广度优先搜索算法,它使用…

CentOS下python-mysqldb安装

CentOS下python-mysqldb安装日期:2011-04-17 | 来源:未知 | 作者:redice | 869 人围观 | 1 人鼓掌了!鲲鹏Web数据抓取 - 专业Web数据采集服务提供者(1)py…

I2C总线接上拉电阻的原因

I2C为什么要接上拉电阻?因为它是开漏输出。为什么是开漏输出?I2C协议支持多个主设备与多个从设备在一条总线上,如果不用开漏输出,而用推挽输出,会出现主设备之间短路的情况。所以总线一般会使用开漏输出。为什么要接上…

解决循环引用--弱引用weak_ptr

循环引用&#xff1a; 引用计数是一种便利的内存管理机制&#xff0c;但它有一个很大的缺点&#xff0c;那就是不能管理循环引用的对象。一个简单的例子如下&#xff1a; class parent; class children;typedef shared_ptr<parent> parent_ptr; typedef shared_ptr<ch…

第九章 虚拟内存

物理地址和虚拟地址&#xff1a; 计算机的主存被组织成一个由M个连续的字节大小的单元组成的数组。每个字节都有一个唯一的物理地址&#xff08;PA&#xff09;。第一个字节地址为0&#xff0c;接下来为1&#xff0c;再接下来为2&#xff0c;依次类推。CPU访问内存的最自然方式…

Android HandlerThread 总结使用

Android HandlerThread 总结使用转载请标明出处&#xff1a;http://www.cnblogs.com/zhaoyanjun/p/6062880.html本文出自【赵彦军的博客】前言以前我在 【Android Handler、Loop 的简单使用】 介绍了子线程和子线程之间的通信。很明显的一点就是&#xff0c;我们要在子线程中调…

python 文件操作练习

1. 文件a.txt内容&#xff1a;每一行内容分别为商品名字&#xff0c;价钱&#xff0c;个数。 apple 10 3 tesla 100000 1 mac 3000 2 lenovo 30000 3 chicken 10 3 通过代码&#xff0c;将其构建成这种数据类型&#xff1a;[{name:apple,price:10,amount:3},{name:tesla,price:…

微软发布Enterprise Library 4.1和Unity 1.2

说明 微软模式与实践团队今天发布了Enterprise Library 4.1和Unity 1.2版本&#xff0c;这次发布的主要新特性如下&#xff1a; 1. 支持Visual Studio 2008 SP1 2. Unity应用程序块中加入了拦截机制 3. 性能提升 4. 配置工具的可用性提升 5. Bugs修复。 在Unity 1.2中有如下几点…

简易的波形折叠电路

01 波形折叠一、背景介绍今天在 Youtube 上看到 UP 主 Neukolln 展示了一款非常简单波形折叠电路。那么什么是波形折叠电路&#xff1f;它有什么用处&#xff1f;1、什么是波形折叠&#xff1f;通常情况下&#xff0c;信号在放大中会产生“饱和失真”&#xff0c;这是由于放大器…

STL 容器和迭代器连载6_顺序容器的操作3

2019独角兽企业重金招聘Python工程师标准>>> /*- * 文件名 &#xff1a;STL_con_ite_6.cpp * 开发人员&#xff1a;袁培荣 * 当前版本&#xff1a;1.0.0.2595 * 创建时间&#xff1a;2012-05-24 * 修改时间&#xff1a;2012-05-24 * 功能…

vector的内存释放

1. vector容器的内存自增长 与其他容器不同&#xff0c;其内存空间只会增长&#xff0c;不会减小。先来看看"C Primer"中怎么说&#xff1a;为了支持快速的随机访问&#xff0c;vector容器的元素以连续方式存放&#xff0c;每一个元素都紧挨着前一个元素存储。设想一…

poj 2096 , zoj 3329 , hdu 4035 —— 期望DP

题目&#xff1a;http://poj.org/problem?id2096 题目好长...意思就是每次出现 x 和 y&#xff0c;问期望几次 x 集齐 n 种&#xff0c;y 集齐 s 种&#xff1b; 所以设 f[i][j] 表示已经有几种&#xff0c;转移一下即可。 代码如下&#xff1a; #include<cstdio> #incl…

[C++]MySQL数据库操作实例

由于课程大实验需要使用c操作MySQL数据库&#xff0c;经过一番研究终于成功实现VS2008中与MySQL的连接。环境设置&#xff1a;安装完MySQL之后&#xff0c;将安装目录中的include目录下的libmysql.lib文件拷到VS2008安装目录中的VC\lib\下&#xff0c;然后在项目-选项-c/c-常规…

MapInfo开发心得——控件篇

前阵做一个项目过程中&#xff0c;需要结合MapInfo进行地方展示开发&#xff0c;积累一点点心得与大家共享以下所有基于MapXtreme 6.6MapInfo提供了足够强大的WinForm地图空间&#xff0c;可以很方便地在VS中拖拉摆放。但如此强大的控件也带来了封闭性太强的问题。现在我们都接…

89个嵌入式相关概念,你懂了几个?

嵌入式、计算机的学习&#xff0c;最基础的就是弄清一些概念。嵌入式、计算机相关的概念很多&#xff0c;这次汇总整理一些&#xff1a;1. 操作系统&#xff08;Operating System&#xff0c;OS&#xff09;&#xff1a;是管理计算机硬件与软件资源的系统软件&#xff0c;同时也…

使用VLC转码,在HTML5页面播放实时监控

首先要获取摄像机品牌的RTSP地址&#xff1a; 如大华的是rtsp://user:pwdip:port/cam/ realmonitor?channel1&subtype0 海康的是rtsp://user:pwdip:port/MPEG-4/ch1/main/av_stream Html5 <video>并不支持rtsp,所以使用vlc进行转码,将rtsp转http流,这样<video&g…

聊聊Linux中线程和进程的联系与区别!

大家好&#xff0c;推荐飞哥的一篇文章&#xff01;关于进程和线程&#xff0c;在 Linux 中是一对儿很核心的概念。但是进程和线程到底有啥联系&#xff0c;又有啥区别&#xff0c;很多人还都没有搞清楚。在网上对进程和线程的讨论中&#xff0c;很多都是聚集在这二位有啥不同。…

Docker swarm 实战-部署wordpress

Docker swarm 实战-部署wordpress 创建一个overlay的网络 docker network create -d overlay demo6imq8da3vcwvj2n499k4bwdlt docker network ls NETWORK ID NAME DRIVER SCOPE feea5ba8507b bridge bridge …

比GDB方便n倍的调试工具——CGDB

CGDB 是GDB的前端&#xff0c;在终端窗口中意图形化的形式来调试代码(基于ncurse)&#xff0c;非常方便。相对于GDB来说&#xff0c;可以很大的提高效率。这篇文章就来分享一下CGDB的最基本使用方法&#xff0c;如果是第一次听说&#xff0c;强烈建议您体验一下&#xff0c;一定…

WCF Security userName/Password

1. Transfer SecurityTransfer Security 主要包括三个方面&#xff1a; "消息完整性(Message Integrity)"、"消息机密性 (Message Confidentiality)" 和 "交互验证(Mutual Authentication)"。消息完整性必须确保消息在传输过程中没有被篡改&…