本节书摘来自异步社区《逆向工程权威指南》一书中的第1章1.1节指令集架构,作者【乌克兰】Dennis Yurichev(丹尼斯),更多章节内容可以访问云栖社区“异步社区”公众号查看。
第一部分 指令讲解
逆向工程权威指南
在最初接触C/C++时,我就对程序编译后的汇编指令十分着迷。按照从易到难的顺序,我循序渐进地研究了C/C++语言编译器生成汇编指令的模式。经过日积月累的努力,现在我不仅可以直接阅读x86程序的汇编代码,而且能够在脑海里将其还原成原始的C/C++语句。我相信这是学习逆向工程的有效方法。为了能够帮助他人进行相关研究,我把个人经验整理成册,以待与读者分享。
本书包含大量x86/x64和ARM框架的范例。如果读者熟悉其中某一种框架,可以跳过相关的篇幅。
第1章 CPU简介
逆向工程权威指南
CPU是执行程序机器码的硬件单元。简要地说,其相关概念主要有以下几项。
指令码:CPU受理的底层命令。典型的底层命令有:将数据在寄存器间转移、操作内存、计算运算等指令。每类CPU都有自己的指令集架构(Instruction Set Architecture,ISA)。
机器码:发送给CPU的程序代码。一条指令通常被封装为若干字节。
汇编语言:为了让程序员少长白头发而创造出来的、易读易记的代码,它有很多类似宏的扩展功能。
CPU寄存器:每种CPU都有其固定的通用寄存器(GPR)。x86 CPU里一般有8个GPR,x64里往往有16个GPR,而ARM里则通常有16个GPR。您可以认为CPU寄存器是一种存储单元,它能够无差别地存储所有类型的临时变量。假如您使用一种高级的编程语言,且仅会使用到8个32位变量,那么光CPU自带的寄存器就能完成不少任务了!
那么,机器码和编程语言(PL)的区别在哪里?CPU可不像人类那样,能够理解C/C++、Java、Python这类较为贴近人类语言的高级编程语言。CPU更适合接近硬件底层的具体指令。在不久的将来,或许会出现直接执行高级编程语言的CPU,不过那种尚未问世的科幻CPU必定比现在的CPU复杂。人脑和计算机各有所长,如果人类直接使用贴近硬件底层的汇编语言编写程序,其难度也很高——因为那样很容易出现大量的人为失误。可见,我们需要用一种程序把高级的编程语言转换为CPU能受理的底层汇编语言,而这种程序就是人们常说的编译器/Compiler。
1.1 指令集架构
在x86的指令集架构(ISA)里,各opcode(汇编指令对应的机器码)的长度不尽相同。出于兼容性的考虑,后来问世的64位CPU指令集架构也没有大刀阔斧地摒弃原有指令集架构。很多面向早期16位8086 CPU的指令,不仅被x86的指令集继承,而且被当前最新的CPU指令集继续沿用。
ARM属于RISC[1] CPU,它的指令集在设计之初就力图保持各opcode的长度一致。在过去,这一特性的确表现出了自身的优越性。最初的时候,所有ARM指令的机器码都被封装在4个字节里[2]。人们把这种运行模式叫作“ARM模式”。
不久,他们就发现这种模式并不划算。在实际的应用程序中,绝大多数的CPU指令[3]很少用满那4个字节。所以他们又推出了一种把每条指令封装在2个字节的“Thumb”模式的指令集架构。人们把采用这种指令集编码的指令叫作“Thumb模式”指令。然而Thumb指令集并不能够封装所有的ARM指令,它本身存在指令上的局限。当然,在同一个程序里可以同时存在ARM模式和Thumb模式这两种指令。
之后,ARM的缔造者们决定扩充Thumb指令集。他们自ARM v7平台开始推出了Thumb-2指令集。Thumb-2指令基本都可封装在2个字节的机器码之中,2个字节封装不下的指令则由4字节封装。现在,多数人依然错误地认为“Thumb-2指令集是ARM指令集和Thumb指令集的复合体”。实际上,它是一种充分利用处理器性能、足以与ARM模式媲美的独立的运行模式。在扩展了Thumb模式的指令集之后,Thumb-2现在与ARM模式不相上下。由于Xcode编译器默认采用Thumb-2指令集编译,所以现在主流的iPod/iPhone/iPad应用程序都采用了Thumb-2指令集。
64位的ARM处理器接踵而至。这种CPU的指令集架构再次使用固定长度的4字节opcode,所以不再支持Thumb模式的指令。相应地,64位ARM工作于自己的指令集。受到指令集架构的影响,ARM指令集分为3类:ARM模式指令集、Thumb模式指令集(包括Thumb-2)和ARM64的指令集。虽然这些指令集之间有着千丝万缕的联系,需要强调的是:不同的指令集分别属于不同的指令集架构;一个指令集绝非另一个指令集的变种。相应地,本书会以3种指令集、重复演示同一程序的指令片段,充分介绍ARM应用程序的特点。
除了ARM 处理器之外,还有许多处理器都采用了精简指令集。这些处理器多数都使用了固定长度的32位opcode。例如MIPS、PowerPC和Alpha AXP处理器就是如此。
[1] Reduced instruction computing /精简指令集。
[2] 这种固定长度的指令集,特别便于计算前后指令的地址。有关特性将在13.2.2节进行介绍。
[3] 即MOV/PUSH/CALL/Jcc等指令。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。