我们知道,我们平时编程写的高级语言,是经过编译器编译以后,变成了CPU可以执行的机器指令:
而CPU能支持的指令,都在它的指令集里面了。
很久以来,我都在思考一个问题:
CPU有没有未公开的指令?
或者说:
CPU有没有隐藏的指令?
为什么会有这个问题?
平常我们谈论网络安全问题的时候,大多数时候都是在软件层面。谈应用程序的漏洞、后端服务的漏洞、第三方开源组件的漏洞乃至操作系统的漏洞。
但很少有机会去触及硬件,前几年爆发的熔断和幽灵系列漏洞,就告诉我们,CPU也不是可信任的。
要是CPU隐藏有某些不为人知的指令,这是一件非常可怕的事情。
如果某一天,某些国家或者某些团体组织出于某种需要,利用这些隐藏的指令来发动攻击,后果不堪设想。
虽然想到过这个问题,但我一直没有付诸实践去认真的研究。
直到前段时间,极客时间的一位老师分享了一份PDF给我,解答了我的疑惑。
这份PDF内容是2017年顶级黑客大会Black Hat上的一篇报告:《us-17-Domas-Breaking-The-x86-ISA》,作者是大神:@xoreaxeaxeax,熟悉汇编的同学知道这名字是什么意思吗?
这份PDF深度研究了x86架构CPU中隐藏的指令,原报告因为是英文,看起来有些晦涩,这篇文章,我尝试用大家易懂的语言来给大家分享一下这篇非常有意思的干货。
有些人会问:真的会有隐藏指令的存在吗,CPU的指令集不是都写在指令手册里了吗?
我们以单字节指令为例,单字节的范围是0x00-0XFF,总共256种组合,Intel的指令手册中是这样介绍单字节指令的:
横向为单字节的高四位,纵向为单字节的低四位,顺着表格定位,可以找到每一个单字节指令的定义。比如我们常见的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亿多种组合,对于计算机来说,也还能接受。
但越往后,容量是呈指数型增长,这种情况再去遍历,显然是不现实的。
指令搜索算法这份报告中提出了一种深度优先的搜索算法: