APP下载

利用页面隐藏机制保护内存数据

2019-12-04周洪伟原锦辉杜遵良

小型微型计算机系统 2019年11期
关键词:存储空间内核内存

周洪伟,原锦辉,肖 锐,杜遵良,冯 贤

1(信息工程大学,郑州 450001)2(中原工学院,郑州 450007)

1 引 言

由于计算机体系结构的限制,现有主流处理器都只能处理明文形式的指令和数据,数据只能以明文形式存在于内存.此外,由于金字塔似的存储体系以及操作系统的影响,使得一些敏感的用户隐私数据在用户未知的情况下,在内存中反复拷贝并长时间存留[1,2].文献[3]提出对内存数据的攻击方法,并以APACHE和OPENSSL为攻击对象,实验结果表明可以在1-5分钟内从内存中窃取目标进程的密钥.

如何保护在内存中的数据?有的学者提出构建安全处理器这样的特殊硬件来保护用户敏感数据[4,5].但是这种方法对硬件要求过高,一旦对硬件有特殊要求就很难被市场接受.有的学者提出利用虚拟机监控器的支持,对内存数据实施保护[6].然而,虚拟机监控器自身的性能开销过大,会影响用户的可接受性.有的学者通过改造应用软件,将安全敏感数据从内存转移到处理器寄存器中.但是处理器寄存器容量有限,无法支持大数据量的保护需求.有的学者则对操作系统进行改造,使活动在内存中的数据,只有在被处理器访问前后的一段时间内,才以明文形式存在于内存[8,9].然而,页面加解密操作的引入使得系统性能开销增加.

本文基于操作系统改造,提出通过隐藏页面的方法,来保护内存数据.与现有方法不同的是,我们不采用加解密算法实施保护,而是通过修改页表,影响虚拟地址寻地过程,使得包含安全敏感数据的页面只有在处理器访问时才出现在进程地址空间,而其它时间由内核单独持有,从而减少安全敏感数据的暴露时间.我们在Fedora 21(内核版本号:3.17.6)上构建了原型系统,并对其进行功能测试和性能测试.测试结果表明:我们的原型系统能够在不影响处理器访问的前提下,隐藏用户隐私数据;虽然对数据读写的操作性能影响明显,但是对系统整体性能影响不大.

2 相关工作

我们从硬件、虚拟机、操作系统和应用软件四个角度说明本文的相关工作.首先是基于硬件的相关工作.保护内存数据的直接方法就是改变处理器不能处理密文数据的状态,典型的范例如AEGIS[4].AEGIS安全处理器是在传统处理器的基础上,增加了加密单元、完整性验证单元、安全上下文管理器以及私钥.进程数据在进入安全处理器时,会经过加解密单元进行解密,解密后的结果会再经过完整性验证单元检查其完整性,而后进入cache和寄存器,最后提交给运算单元完成计算.当进程数据处理完毕后,会再次经过完整性验证单元和加解密单元,最终形成加密保护和完整性保护的数据,存储于不可信的内存.这样,攻击者就无法直接从内存中窃取信息.类似的工作还有:XOM[5]、Cerium[10]和RIFLE[11]等.这类工作面临的最大问题是:它们所依赖的硬件并不一定会被商用和推广.这就导致这些解决方案的用户可接受程度不高.

虚拟化技术的发展为内存数据保护提供新的途径.Overshadow[12]就是一个典型案例,其原理是利用虚拟机监控器对访问物理内存的控制,将内存的内容以不同形式展现给安全应用和操作系统.当安全应用访问其内存时,内存内容是明文.当操作系统或其它应用程序访问这块内存时,内存内容则是密文.Storage Capsules[6]依赖虚拟化技术的支持,将数据处理的操作系统置于私有的虚拟机客户机中,并在虚拟机监控器控制私有客户机对硬件的访问.在用户数据编辑前,虚拟机监控器记录客户机状态,并在用户数据编辑过程中,禁止客户机外泄用户数据,用户数据被编辑完毕后,虚拟机监控器还原私有客户机,仅保留数据处理结果.类似的工作还有Lacuna[13]、SeCage[14]等.基于虚拟机监控器的解决方案面临的主要问题是虚拟机监控器自身的性能开销过大,将虚拟机监控器引入计算机系统会导致整体性能下降,影响用户可接受性.

改造操作系统,提升其安全性是保护内存数据的一种途径.RamCrypt[8]修改内核,在内存页面访问增加新的异常,使得处理器访问密文页面时,首先由操作系统内核对其进行脱密,再转交处理器访问.按照RamCrypt的设计,大多数内存页面在大多数时候都是呈密文状态.Cryptkeeper[9]和LeMe[15]是与RamCrypt类似的工作.Cryptkeeper将内存页面分为一个小的明文状态的页面集和大的密文状态的页面集.在初始情况下,所有页面是公开的;在页面置换过程中,公开页面逐渐转换为秘密页面;当秘密页面过多,再转换成公开页面.LeMe则是一种轻量级内存加解密方案,它仅对用户所标识的数据实施内存加密保护.Shreds[16]是对进程结构的改造,实现进程内部更细粒度的隔离,达到只有特定指令才能访问特定数据的目的.Nizza[17]依托L4微内核,为上层应用构建可信的运行环境.类似的还有Virtual Ghost[18],它是在不可信操作系统基础上保护进程,其核心思想是:依赖编译优化和运行时检查,构造一个内核不能访问的存储空间,使得进程数据与不可信内核相互隔离.

还有一些工作是通过改变应用软件,将用户隐私数据隐藏在非内存的存储区域.就目前的相关工作来看,处理器寄存器是存储内存秘密数据的后备存储地.AESSE[19]为了不使AES密钥出现在内存,AESSE占用SSE相关的寄存器xmm0-xmm7,在处理器内部完成AES加解密运算.TRESOR[20]使用调试寄存器来保存AES密钥,Amnesia[21]是与TRESOR类似的工作,它也将密钥存储于处理器寄存器中,并重新设计加密算法,保证加密算法正常运算.处理器寄存器容量有限,无法直接存储RSA这样较长的非对称密钥.Copker就是应对这一问题的一种解决方案[22].在初始情况下,Copker借助TRESOR的方法保护主密钥.主密钥是AES对称密钥,用于加密保护私钥,私钥的密文存在于内存.工作时,Copker基于主密钥解密获得私钥的明文,然后再基于私钥完成相应的密码运算.PRIME[23]也实现类似的工作.Mimosa[24]在TSX特性的支持,将RSA私钥的密文以及保护密钥读入cache,在保护环境下完成RSA私钥的解密以及RSA运算.PixelVault[7]将密钥存储于GPU寄存器中,并且敏感代码也存在于GPU的cache.依赖GPU设置不可抢断执行模式的特点,保证加解密过程中密钥不泄露到内存.

3 方法设计

页面隐藏就是指在内存秘密数据不被进程访问时,将其从进程地址空间移走,从而使攻击者无法从进程地址空间中获取秘密数据.为此,在保证处理器访问内存数据不受影响的前提下,通过及时的页面转置,使待访问数据在敏感页面和影子页面之间切换,使秘密数据长时间不存在于进程地址空间,从而实现对秘密数据的保护.

3.1 工作原理

结合图1说明本方法的工作原理.我们通过改造Linux操作系统的内存管理子系统,为进程地址空间构造两个视图,使得攻击者和进程本身所看到的地址空间视图不一样.在攻击者视图内,不包括需要保护数据的内存页面.需要保护数据所在内存页面只出现在进程视图内.

图1 工作原理Fig.1 Working principle of our solution

为了便于说明,我们给出以下定义:

定义1.敏感页面:敏感页面是指包括需要保护数据的页面.

定义2.影子页面:影子页面是指与敏感页面对应的页面,它将用于替代敏感页面,构建攻击者视图.

为了欺骗攻击者,在攻击者视图中,我们用影子页面替代敏感页面.影子页面与敏感页面基本一致,只是对需要保护的数据进行伪造.例如:敏感页面记录用户口令是pwd,影子页面则记录用户口令是usr.通过欺骗攻击者,使其难以查觉敏感页面的存在.

为了保证处理器正常访问进程的指令和数据,我们通过页面转置操作,及时完成两种视图的切换.当进程申明需要保护的内存数据后,在系统为这些内存数据分配内存页面的同时,也会构造影子页面与敏感页面一一对应.在攻击者视图中,敏感页面将由内核持有,其页表项信息并不会出现在进程页表中,这样攻击者就无法从进程页表中查询到敏感页面.当进程需要访问被保护的内存数据时,内核将敏感页面的页表项覆盖影子页面的页表项,实现攻击者视图到进程视图的切换.反之,当内核将影子页面的页表项覆盖敏感页面的页表项时,则实现进程视图到攻击者视图的切换.这种切换我们称之为页面转置.由于页面转置只涉及到页表项的修改,而x86系列处理器所对应的页表项的大小只有32字节,对其操作较加解密运算要节省性能开销.这样,我们就可以在不过多影响系统性能的前提下达到保护内存数据的目的.

3.2 系统架构

本方法的系统架构如图2所示.主要的功能单元均在内核空间,只有用户接口在用户空间,且只对用户开放.内核空间有页面转置、页面分配与页面回收功能单元.用户接口以动态链接库的形式提供给用户.用户接口与内核中的功能单元,通过系统调用的方式完成交互.

图2 系统架构图Fig.2 Architecture of our solution

分别简要说明各功能单元.用户接口是用户使用本系统的接口,用户可以通过用户接口访问本系统所提供的服务,如向页面转置功能单元提出页面转置请求,由其完成敏感页面的重现和隐藏.页面转置功能单元是指实现敏感页面和影子页面之间的相互替代,包括页面正转和页面逆转两种情况.页面分配与回收并非完全独立功能单元,而是对现有Linux操作系统内核的改造,从而满足页面分配和回收的特殊要求.页面分配是指为待保护内存数据的页面分配操作,当分配一个敏感页面存储待保护数据时,系统同时分配一个与之同样大小空间的影子页面用来存储虚假数据.页面回收功能单元采用双页面回收算法,同时回收敏感页面及与之对偶的影子页面.

3.3 页面分配

本文所讨论页面分配是指为待保护内存数据的页面分配操作.如果用户提出对某项数据的保护需求,我们将对其作特殊处理,以支持后继的页面隐藏相关操作.页面分配将分为以下几个步骤.

第1步.申请页面.内存数据需要申请新的存储空间时,系统按照传统的方式申请相应的存储空间,将其视为敏感页面;同时为影子页面分配相应的存储空间;标记影子页面和敏感页面的对偶关系.

第2步.数据虚假化.当内存数据被拷贝到敏感页面后,本系统要求将类似的内容拷贝至影子页面,以欺骗攻击者.用D表示待保护的内存数据,用S表示敏感页面的其它数据,那么影子页面所包含的数据为D′∪S,其中D′是故意伪造的虚假信息.

第3步.页面隐藏.为了隐藏敏感页面,我们将敏感页面的信息从页表中移除,将其转移至影子页表.影子页表的定义如定义3所示.

定义3.影子页表:影子页表是保存敏感页面和影子页面页表项及对偶关系的数据结构.

与本文所使用的影子页表容易混淆的是虚拟机监控器所使用的影子页表.虽然两个页表都保存了页表项信息,但是本文所使用的影子页表只是单纯保存敏感页面和影子页面的页表项信息,并记录敏感页面和影子页面的对偶关系,这些信息是实现页面转置时,供本系统查询使用,影子页表本身并不参与寻址.

如图3所示,页表既含有普通页面(非敏感页面和影子页面的其它页面)的页表项信息,也可能含有敏感页面和影子页面的信息.在绝大部分情况下,敏感页面的页表项并不出现在页表中,都以影子页面的形式出现.影子页表记录了影子页面和敏感页面对换所需要的信息.

图3 页面隐藏示意Fig.3 Method to hide page

存在一些特殊情况需要进一步说明.第一种情况是:新的待保护数据将写入敏感页面.在这种情况下,后继数据将不需要再申请页面,但应该再次申明待保护数据,以便系统明确该页面含有超过1个受保护数据.第二种情况是:待保护数据占据多个页面.这种情况需要 将多个敏感页面的信息从页表中移除,将其转移至影子页表.以上两种情况都不会影响页面隐藏.

3.4 页面转置

首先给出页面转置的定义.

定义4.页面转置:页面转置是指敏感页面和影子页面之间的相互替代.

当处理器对待保护数据需要进行正常访问时,系统会使用敏感页面替代影子页面.当处理器结束访问后,系统会将影子页面替代敏感页面,从而避免敏感页面长时间暴露.页面转置并不是真的将页面实现替代,而是将影子页面和敏感页面所对应的页表项实施替代,从而改变处理器寻址的目的页面.由于页表项长度较页面要小得多,从而可以避免在内核空间大规模转移数据,进而提高系统效率.页面转置分为页面正转和页面逆转两类,其定义如下:

定义5.页面正转:页面正转是指敏感页面替代影子页面,以隐藏敏感页面内的待保护数据.

定义6.页面逆转:页面逆转是指影子页面替代敏感页面,以满足处理器访问敏感页面内待保护数据的合法请求.

页面正转和页面逆转相互配合,共同完成敏感页面内待保护数据的隐藏和正常访问.页面正转在合法访问待保护数据前发生,页面逆转是在合法访问待保护数据后发生.当回收页面时,我们通过页面正转恢复页表以完成后继内存释放操作.

页面转置请求由用户显式提出.页面转置也可以由页面异常引起,但是我们认为这可能导致风险.操作系统内核无从得知当前处理器访问是否合法.如果只要处理器访问(这就可能引起页面异常),系统内核就实施页面转置,那隐藏页面就没有意义可言,因为攻击者可以引导触发处理器访问该页面,从而获知敏感页面的内容.为此,我们强调页面转置请求应该由用户显式提出.用户在访问待保护数据时,首先提出页面转置请求,待系统内核完成转置后,再实施访问,待访问结束后再次提交页面转置请求,恢复影子页面.

页面转置请求由用户显式提出也存在弊端.页面正转操作和页面逆转操作应该对偶,且页面逆转操作应该在完成数据访问后立即执行.不过,用户可能会遗忘页面逆转操作,或者没有及时执行页面逆转操作,从而导致敏感页面长期处于暴露状态.为此,我们增设页面定时自动逆转功能.当用户执行页面正转操作后,超过一定时间仍然没有执行页面逆转操作,系统就自动执行页面逆转操作,从而减少敏感页面暴露风险.

3.5 页面回收

页面回收的目的是回收影子页面和敏感页面从而释放内存空间.页面分配时,我们根据敏感页面的情况,额外多分配相应页面以建立影子页面.如果仍然按照传统的页面回收方法,是无法完全回收额外分配的页面,只能回收出现在页表中的页面.一种解决方案是待进程死亡时,根据影子页表回收页面.但是如果进程长久存活,且反复申请和回收页面,就会造成大量的内存页面无法使用的情况.因此需要对传统的页面回收功能进行升级和改造.

首先说明双页面回收算法.该算法需要同时回收敏感页面以及与之对偶的影子页面,故而称之为双页面回收算法.双页面回收算法不仅需要回收敏感页面,还需要回收影子页面以及影子页表对应结点.其工作流程如下:在回收具体页面前,首先应查询影子页表,遍历影子页表获知目标页面信息(包括敏感页面和影子页面);执行页面正转,恢复进程页表,以便后继内存释放操作;然后利用传统内存释放方法释放敏感页面和影子页面;最后释放影子页表中对应的结点.

还存在影子页面回收算法.影子页面回收算法是针对这样的一种回收场景:用户在删除受保护数据前,将敏感页面恢复成普通页面.我们称这种情况下的页面回收算法为影子页面回收算法.简而言之,影子页面回收算法只回收影子页面,保留敏感页面.当敏感页面不再包含任何受保护数据时,系统应该将影子页面提前删除.其工作流程如下:系统检查当前页表中相应页面是否为影子页面,如果是则执行页面正转;系统释放影子页面,并修改影子页表,释放相应节点;此时意味着原敏感页面转变为普通页面.如果用户需要继续回收页面,则按照传统页面回收算法回收页面即可.

4 系统实现

我们在Fedora 21(Linux内核版本号:3.17.6)构建原型系统.为了实现页面分配、回收和转置的操作,我们修改了内核,添加了5个内核函数,封装了4个系统调用,以动态链接库的形式向用户提供7个接口函数,以便用户在编程时标注需要保护的数据,并完成正转和逆转等操作.

4.1 页面分配和回收

由于我们为每个敏感页面附加一个影子页面,从而使得我们必须改变传统的内存分配方法以及回收方法.内存分配有多种形式,出于典型示范的考虑,我们选择对malloc和free这种堆分配和回收操作进行升级,来适应本系统.为此,我们新增ppmalloc、ppfree和ppclear实现页面分配和回收.为了支持上述用户接口,我们设计和实现了ppinit和ppfree两个系统调用和ptp_addnode、ptp_delnode和get_pte三个内核函数.

ppmalloc工作流程如图4(a)所示:

①首先利用传统的malloc申请敏感页面存储空间;

②利用传统的malloc申请影子页面存储空间;

③调用ppinit系统调用,ppinit进一步调用内核函数ptp_addnode,在影子页表中添加一个新的空白节点;

④调用内核函数get_pte,收集敏感页面和影子页面的页表项,填写新分配节点的内容.与传统的malloc相比,ppmalloc所分配的存储空间增加,同时也增加了维护影子页表的操作.

图4 ppmalloc和get_pte流程Fig.4 Workflow of ppmalloc and get_pte

为了让内核感知影子页表,我们在进程描述符数据结构task_struct中增加一项属性(用*Phead表示),指向影子页表.影子页表以单链表的形式组织,每个节点的定义如下所示:

strcut ptp_node{

unsigned char* addr_t;

unsigned char* addr_f;

PTE pte_t;

PTE pte_f; ptp_node * next;

}

每个节点内部两个页表项pte_f和pte_t分别对应影子页面和敏感页面,两者一一对应,呈对偶关系.addr_t是敏感数据地址,其位于敏感页面内,addr_f是虚假数据地址,其位于影子页面内.页面转置时,需要查询影子页表,其性能开销与影子页表长度相关.由于影子页表所含节点数量一般不多,我们认为查询操作不会影响到系统整体性能.

内核函数ptp_addnode的功能是在影子页表中添加一个新的节点,其函数原型为:int ptp_addnode(char* t_addr,char* f_addr).与之相对应的是内核函数ptp_delnode,该函数用于在影子页表中删除指定节点,其函数原型为:int ptp_delnode(char* t_addr).由于影子页表采用单链表的形式组织,所以从本质上讲,上述两个函数就是在单链表中添加或删除节点,其实现过程较为简单,本文不再赘述.

内核函数get_pte是支持ppmalloc的另一个内核函数,其函数原型是pte_t* get_pte(unsigned long addr),其功能是获取指定地址所在页面的页表项.该内核函数的工作流程如图4(b)所示:

①以输入地址addr为参数,利用内核函数pgd_offset,获取页全局目录描述符;

②利用内核函数pud_offset,进一步获取页上级目录描述符;

③利用内核函数pmd_offset,进一步获取页中间目录描述符;

④利用内核函数pte_offset_map,最终获取页表项.内核函数ptp_addnode利用该函数获取敏感页面和影子页面的页表项信息,并填入影子页表中新添加的空白节点.

当敏感的内存数据写入敏感页面后,类似的内容也应该写入影子页面,以欺骗攻击者.构造虚假信息的一种直接而简单的方法是采用随机数对其进行覆盖.不过,这种方法容易被攻击者识破,进而促使攻击者实施进一步攻击.我们设计和实现了一个新的用户接口pptrap,由用户指定写入影子页面的虚假数据.虚假数据的大小和所在页内的偏移应该与敏感数据相同,从而使数据以一种接近真实的状态呈现给攻击者.例如敏感页面记录用户口令是pwd,影子页面则记录用户口令是usr,攻击者窃取到usr口令时认为攻击已经成功,进而停止攻击,从而使页面达到较好的隐藏效果.

图5 ppfree和ppclear流程Fig.5 Workflow of ppfree and ppclear

为了支持真实页面和虚假页面的回收,我们设计和实现了用户接口ppfree和ppclear.ppfree对应双页面回收算法,其工作流程如图5(a)所示:

①执行敏感页面转置操作(详见4.2节),确保敏感页面处于正转状态,还原页表项;

②调用ppfree系统调用,触发内核函数ptp_delnode,删除影子页表所保存的对应节点;

③利用传统的free释放对应的影子页面;

④利用传统的free释放对应的敏感页面.

与传统的free相比,ppfree删除的存储空间增加,并且增加删除影子页表节点的操作.ppclear对应影子页面回收算法,其工作流程如图5(b)所示,与ppfree主要的区别在于不再释放敏感页面.完成ppclear操作后,敏感页面所对应的影子页面将不存在,而敏感页面本身则改变为普通页面.

4.2 页面转置操作

页面转置操作分为页面正转操作和页面逆转操作.虽然目的是实现影子页面和敏感页面的相互替代,但是在本质上是通过修改页表项内容,影响虚拟地址寻址过程,使同样的虚拟地址在页面转置前后,会出现不同的寻址结果,定位于不同的物理页面.由于页表项长度较页面要小得多(页面大小一般为4K字节,而页表项的大小为32字节),从而可以避免在内核空间大规模转移数据,进而提高系统效率.

为了便于用户控制页面转置操作,我们设计和实现了ppturnoff和ppturnon接口函数.上述支持上述应用接口,我们在系统调用层面增加了ppturnoff和ppturnon两个系统调用,在内核函数层面增加了put_pte和ptp_findnode两个内核函数.put_pte的函数原型:int put_pte(unsigned long addr,pte_t *pte_n),其功能是用pte_n替换页表中addr所在页面的页表项,其实现类似内核函数get_pte,本文不再赘述.ptp_findnode的函数原型:struct ptp_node* ptp_findnode(char* addr),其功能是查找影子页表指定节点,用于获取该节点所存储的敏感页面和影子页面的页表项信息,其实现是遍历影子页表,并按条件返回指定节点.

页面正转操作是使用敏感页面的页表项替代影子页面的页表项,从而实现还原数据的目的.其实现的细节如下:

①用户通过接口函数ppturnoff发出页面正转请求;

②该请求触发系统调用ptp_turn_off;

③系统调用ptp_turn_off调用内核函数ptp_findnode,该函数利用Current宏,获取当前进程任务描述符,从而获得影子页表头指针,进而遍历影子页表,获得对应地址所在敏感页面的页表项pte;

④系统调用ptp_turn_off调用新增加的内核函数put_pte,获得对应地址所在页面的页表项pte',并利用内核函数set_pte,使用pte替代pte',改变进程页表内容;

⑤刷新cache,确保当前页表项更新生效.如果不刷新cache,会使处理器仍然使用pte'进行寻址,使得页面正转无法生效.

页面逆转操作与页面正转操作正好互逆,是用影子页面的页表项替换敏感页面,从而实现隐藏数据的目的,其实现的细节类似于页面正转,如下所述:

①用户通过接口函数ppturnon发出页面逆转请求;

②用户请求触发系统调用ptp_turn_on;

③ptp_turn_on调用内核函数ptp_findnode,获得对应地址所在影子页面的页表项pte';

④ptp_turn_on调用内核函数put_pte,该函数查询页表,获得对应地址所在页面的页表项pte,并利用内核函数set_pte,使用pte'替代pte;

⑤刷新cache,确保当前页表项更新生效.

为了避免由于用户的遗漏,没有及时执行页面逆转操作,从而使数据长时间暴露,我们增加用户接口pptime.用户接口pptime用于实现延时自动逆转的功能,允许在页面正转后若干时间后,自动执行页面逆转操作.当然,用户再次执行页面逆转操作,也不会引起系统异常,所有的操作结果均以最后一次执行为准.

4.3 用户接口和函数调用关系

表1总结了本系统为用户所封装7个接口函数.其中,ppmalloc和、ppfree和ppclear是用户分配和回收存储空间时使用;ppturnon和ppturnoff是用户实现页面转置的接口;pptime是用户设置延时自动转置的接口;pptrap用于用户设置为影子页面设置虚假内容.pptime和pptrap是在用户空间完成,不与新增系统调用交互.

我们整理了系统实现过程中,所增设的内核函数、系统调用和用户接口,图6描述了它们之间的调用关系.在内核函数层面,我们添加了get_pte和put_get两个内核函数用于操作传统页表中的页表项(即:PTE),为了操作影子页表,我们增加了ptp_addnode等3个内核函数.在系统调用层面,我们增加了4个系统调用,分别对应页面初始化、页面正转、页面逆转和页面释放4种操作.依托4个新增加的系统调用,我们设计和实现了5个用户接口函数,同时从提高安全性角度,我们另外增加延时反转和设置虚假数据等2个用户接口.

表1 用户接口Table 1 User interfaces

5 评 估

我们从功能和性能两个方面对原型系统进行测试.在功能上,我们通过在内存中对敏感页面以及对影子页面的访问结果进行对比,进而表明在内存中只有对敏感页面的访问才是有效的.在性能上,我们利用测试软件UnixBench对引入原型系统后的操作系统进行整体测试,并通过测试页面转置函数执行时间来进一步分析页面转置性能开销,从而全面反映原型系统的性能开销情况.

表2 软硬件情况Table 2 Used hardware and software

我们测试所使用的软硬件信息如表2所示.硬件平台可以在市面上购买,软件均为开源软件,可以在相应的网站下载安装.在后继的时间,我们拟在其它的硬件平台以及其它内核版本的操作系统上完成测试,以进一步验证本系统的兼容性.

5.1 功能测试

为验证本系统的有效性,我们编写一段简单应用程序,其内容为打印某个字符串变量.我们将该变量设置为需要保护的数据,并在页面正转、逆转后分别打印该字符串.具体测试流程如下描述.

我们首先利用ppmalloc()申请12字节的存储空间,并对其赋初始值“Hello world”,然后调用pptrap()将类似的虚假数据“I am a liar”写入影子页面.现在打印该存储空间的内容,由于初时状态下存储空间为敏感页面状态,该打印结果应该为“Hello world”.我们调用ppturnoff()将敏感页面逆转为影子页面,再次打印该存储空间的内容,此时存储空间为影子页面状态,该打印结果应该为“I am a liar”.再次调用ppturnon()将影子页面正转为敏感页面,并利用pptime()设置页面定时自动逆转,在规定时间后,再次打印该存储空间内容,打印结果应该为“I am a liar”.

图7 功能测试Fig.7 Functional test

实验结果如图7所示,与预期效果一致.当页面处于敏感页面时,处理器可以直接访问页面内的敏感数据,打印出我们预先写入的字符串数据.当敏感页面被逆转为影子页面时,处理器所访问的是虚假数据.这反映了我们的系统成功改变了地址映射关系,使同一个地址在页面正转和页面逆转操作后,指向不同的存储空间,使攻击者无法在进程地址空间看到敏感数据.

5.2 性能测试

为了测试本系统的性能开销情况,我们利用测试软件UnixBench对引入本系统后的系统进行测试,从而全面反映本系统对计算机整体性能开销的影响.使用UnixBench实施测试的方案如下:首先采用默认设置,在未安装本系统的操作系统上运行UnixBench,记录得分情况(称基准得分);然后安装本系统,再次运行UnixBench,记录得分情况(称系统得分);对比分析两种情况下的UnixBench得分情况.我们以处理器每核同时运行测试程序的结果作为最终结果.为了提高准确率,我们重复实验5次,以平均值作为最后结果.本部分实验结果如表3所示.从结果来看,系统整体性能下降幅度为0.9%左右.考虑到UnixBench自身的误差,可以认为原型系统对系统总体性能开销基本可以忽略.

为了进一步明确性能开销,我们先后测试存储空间分配操作和存储空间写操作的时间开销.对于存储空间分配操作,我们采用本系统所提供函数接口ppmalloc与传统malloc函数接口分配同样大小存储空间的时间开销.为了提高测量精度,我们使用rdtsc指令读取处理器时钟周期计数值,以处理器时钟周期为计数量单位.我们分别使用上述两个函数申请16字节、64字节、256字节等多种大小的存储空间,并记录其所消耗的处理器时钟周期数量.重复实验10次,以最后平均值为最后结果.实验结果如表4所示.从表中的数据可以发现,当分配不大于1024字节存储空间时,ppmalloc的性能开销约为malloc的75倍.当分配的存储空间较大时,性能开销的差距会有明显缩小.

表3 UnixBench测试结果Table 3 UnixBench test results

表4 存储空间分配操作性能测试Table 4 Performance tests for allocating space

我们还对敏感页面写操作进行了性能测试.我们分别使用memset函数将不同大小的存储空间初始化.相对于传统写操作,本方法需要首先对页面进行转置,再进行写操作.同样,为了保证测试结果的准确性,我们重复实验10次,并以平均值作为最后结果.实验结果如表5所示.从表中的数据可以发现,性能开销增加数十倍.

表5 写操作性能测试Table 5 Performance tests for writing

虽然整体性能测试和单项操作性能测试的结果都表明原型系统导致操作性能存在明显的下降,但是这种下降幅度对于进程运行的影响基本可以忽略.我们采用处理器时钟周期作为计量单位,虽然增加的幅度很明显,但是增加的实际值其实是很有限的.就写操作而言,虽然需要20000左右的时钟周期,但是也仅仅只有10微秒左右.考虑到软件需要保护数据的数量以及读写操作的频繁程度,原型系统对软件整体运行的性能基本可以忽略.

分析性能增加的原因.首先是由于页面转置操作.虽然页面转置本身只是对页表项的替换,但是为了在应用空间和内核空间之间的切换,成为性能增加的主要开销.其次是影子页表维护.在页面分配时,由于需要额外分配影子页面和影子页表,这导致分配存储空间的性能开销增加.此外,当被操作存储空间比较大时,性能开销的增加幅度变小,这主要是因为传统方法需要的性能开销增加,而这种增加对于本方法而言并不明显.

对于写操作而言,存在一种性能优化的途径.考虑到页面转置是主要性能开销操作,可以考虑在指定时间内,保持敏感页面在进程地址空间,待多次写操作完毕后再实施页面转置.针对这一问题,我们完成如下实验:在页面转置后,完成1000次写操作,而不必每次写操作前均进行一次页面转置.实验结果表明:在上述大小的存储空间写操作中,性能开销的增加幅度均可以降低至2倍至10倍.

6 结束语

本文提出一种新的内存数据保护方案.该方案能够使处理器在访问敏感数据前后,敏感数据页面才出现在进程地址空间,而其它时间则从进程地址空间移出,由操作系统内核单独持有.这样,敏感数据以明文形式暴露在攻击者面前的时间大幅减少.为了达到上述设计目的,我们通过修改进程页表,影响虚拟地址寻址过程,使得敏感页面和影子页面能够及时转置.我们在Fedora 21(内核版本号:3.17.6)构建了原型系统,并以动态链接库的形式向用户提供7个接口函数,方便用户使用内核新特性.实验验证表明原型系统能够通过页面转置,在不影响处理器访问的前提下,实现敏感数据的隐藏.系统整体性能测试表明原型系统对系统整体性能影响约在1%以下.单项测试表明原型系统存在明显的性能下降.但是这种下降幅度,对于进程整体运行而言,仍然是可以忽略的.

猜你喜欢

存储空间内核内存
多内核操作系统综述①
基于多种群协同进化算法的数据并行聚类算法
强化『高新』内核 打造农业『硅谷』
活化非遗文化 承启设计内核
苹果订阅捆绑服务Apple One正式上线
用好Windows 10保留的存储空间
笔记本内存已经在涨价了,但幅度不大,升级扩容无须等待
“春夏秋冬”的内存
微软发布新Edge浏览器预览版下载换装Chrome内核
内存搭配DDR4、DDR3L还是DDR3?