体系结构视角下控制流完整性保护技术的深度剖析与实践探索_第1页
体系结构视角下控制流完整性保护技术的深度剖析与实践探索_第2页
体系结构视角下控制流完整性保护技术的深度剖析与实践探索_第3页
体系结构视角下控制流完整性保护技术的深度剖析与实践探索_第4页
体系结构视角下控制流完整性保护技术的深度剖析与实践探索_第5页
已阅读5页,还剩88页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

体系结构视角下控制流完整性保护技术的深度剖析与实践探索一、引言1.1研究背景与意义在信息技术飞速发展的当下,计算机系统已深度融入社会生活的各个层面,从日常办公、金融交易,到关键基础设施的运行,其安全性直接关系到个人隐私、企业利益乃至国家的安全与稳定。随着网络攻击手段的不断演进,控制流劫持攻击成为了软件系统面临的重大威胁之一。控制流劫持攻击能够利用软件漏洞,恶意篡改程序的控制流,使程序执行攻击者预先设定的恶意代码,从而获取系统权限、窃取敏感信息或破坏系统正常运行。例如,在早期的攻击中,攻击者常采用代码注入方式,通过缓冲区溢出等漏洞控制程序中函数的返回地址,进而将控制流导向精心布置的恶意代码,如shellcode,实现对系统的非法控制。为抵御此类攻击,现代操作系统在硬件支持下引入了数据执行保护(DEP,Data-Execution-Prevention,如NX或W⊕X机制),通过限制内存页不能同时具备执行权限和写权限,有效阻止了程序运行攻击者注入的恶意数据。然而,攻击者并未就此止步,转而探索出代码重用攻击方式,像Return-to-libc、ROP(ReturnOrientedProgramming)、JOP(JumpOrientedProgramming)等。研究表明,当被攻击程序代码量达到一定规模,攻击者便能从程序中找到图灵完备的代码片段,绕过DEP保护,达成攻击目的。为了抵御控制流劫持攻击,2005年Abadi等人提出了控制流完整性(CFI,ControlFlowIntegrity)保护机制。其核心在于限制程序运行中的控制转移,确保其始终在原控制流图所限定的范围内,通过分析程序控制流图获取间接转移指令(间接跳转、间接调用、函数返回指令)目标的白名单,并在运行时核对间接转移指令的目标是否在白名单中,从而有效防范控制流劫持攻击,保障软件系统安全。CFI技术从实现角度可分为细粒度和粗粒度两类。细粒度CFI严格把控每一个间接转移指令的转移目标,虽能提供高强度的安全保障,但在现有系统环境下,往往会引入较大的开销,影响系统性能;粗粒度CFI则将一组类似或相近类型的目标归为一类进行检查,这种方式虽能降低开销,却在一定程度上牺牲了保护效果。体系结构支持的控制流完整性保护技术研究具有至关重要的意义。从技术发展角度来看,深入研究该技术能够不断完善CFI保护机制,解决当前面临的如开销过大、保护精度不足等问题,推动控制流完整性保护技术向更高效、更精准的方向发展,为软件系统安全提供更坚实的技术支撑。在实际应用中,该技术能够增强各类软件系统的安全性,有效抵御控制流劫持攻击,降低系统遭受攻击的风险,保障个人、企业和国家的信息安全,维护社会的稳定运行。无论是对于保障用户数据隐私、促进企业健康发展,还是维护国家信息安全战略,体系结构支持的控制流完整性保护技术都发挥着不可或缺的作用,其研究成果具有广泛的应用前景和深远的社会价值。1.2国内外研究现状控制流完整性保护技术自提出以来,在国内外均引发了广泛关注,众多学者从不同角度展开研究,取得了一系列具有重要价值的成果。在国外,Abadi等人于2005年开创性地提出控制流完整性(CFI)的防御机制,为后续研究奠定了坚实基础。该机制通过分析程序控制流图获取间接转移指令目标白名单,并在运行时核对目标是否在白名单中,以此保障软件系统安全。此后,相关研究不断深入。在细粒度CFI方面,部分研究致力于严格把控每一个间接转移指令的转移目标,以实现高强度安全保障,如通过精准的指令插桩技术,对程序执行过程中的每一次间接跳转、间接调用和函数返回指令进行细致检查,确保其目标的合法性,有效抵御各类复杂的控制流劫持攻击。然而,这种精细检查在现有系统环境下,往往会引入较大开销,严重影响系统性能,限制了其在实际场景中的广泛应用。为解决细粒度CFI开销过大的问题,粗粒度CFI应运而生。它将一组类似或相近类型的目标归为一类进行检查,有效降低了开销。例如,将具有相似功能或处于同一模块的函数调用目标视为一类,在检查时只需验证目标是否属于该类,而非逐一检查具体目标。但这种方式在一定程度上牺牲了保护效果,无法像细粒度CFI那样精准地抵御某些攻击,存在安全隐患。近年来,国外研究开始注重CFI与硬件技术的结合,利用硬件特性提升保护效率和精度。如Intel的MPK(MemoryProtectionKeys)硬件保护机制,为CFI技术提供了更强大的支持,能够在保障安全性的同时,降低性能损耗。研究人员还探索利用新型硬件架构,如异构计算架构,优化CFI的实现方式,进一步提高系统的整体安全性和性能。在国内,控制流完整性保护技术的研究也取得了显著进展。学者们积极探索适合国内应用场景的CFI技术方案,在多个方面展开深入研究。西安电子科技大学李金库教授团队聚焦程序控制流完整性保护关键技术研究,提出了新方法ECCut。该方法针对已有静态分析方法导致间接调用分析结果不精确的问题,采用完全的字段敏感分析技术和具有源点意识的分析技术,有效提高了程序控制流图的精度,能够防御已有方法无法抵御的控制流劫持攻击,同时在标准基准测试集和实际应用程序上引入的性能开销可接受,平均为7.2%,极大地增强了程序的安全性。国内研究还关注CFI技术在不同领域的应用,如工业控制系统、移动智能终端等。针对工业控制系统中软件控制流转移的特殊性,研究人员提出了相应的CFI保护方案,通过逆向工程分析和构建插桩框架,有效解决了工控软件二进制文件结构不同、加载与传统软件不同以及控制流转移不同等问题,保障了工业控制系统的安全稳定运行。现有研究在控制流完整性保护技术方面取得了丰硕成果,但仍存在一些不足。部分CFI方案在保障安全性和降低性能开销之间难以达到理想平衡,无论是细粒度CFI的高开销问题,还是粗粒度CFI的保护效果欠佳问题,都限制了CFI技术的广泛应用。在复杂多变的网络环境下,一些CFI技术对新型攻击手段的防御能力有待提高,难以应对不断演变的攻击威胁。此外,CFI技术在不同系统和应用场景中的兼容性和可扩展性也需要进一步优化,以满足多样化的安全需求。1.3研究方法与创新点本研究综合运用多种研究方法,从不同维度深入剖析体系结构支持的控制流完整性保护技术。在文献研究方面,全面梳理国内外关于控制流完整性保护技术的相关文献,涵盖学术论文、研究报告、技术标准等。深入分析前人在CFI技术的提出、发展历程中的研究成果,如Abadi等人提出的CFI防御机制的原理和应用;细致研究不同CFI技术方案的特点,包括细粒度CFI对间接转移指令目标的严格把控及其高开销问题,以及粗粒度CFI的分类检查方式和保护效果欠佳的情况;关注CFI技术与硬件结合的研究进展,像Intel的MPK硬件保护机制对CFI的支持。通过广泛而深入的文献研究,准确把握该领域的研究现状和发展趋势,为后续研究提供坚实的理论基础。基于对现有研究的深入理解,本研究采用案例分析方法,选取多个具有代表性的软件系统作为案例。这些案例涵盖不同类型的应用,如工业控制系统、移动智能终端软件等,分析它们在实际运行中面临的控制流劫持攻击风险。例如,针对工业控制系统中软件控制流转移的特殊性,分析其在面对攻击时的脆弱点;研究移动智能终端软件由于其开放性和广泛的用户交互,所面临的多样化控制流劫持威胁。通过对实际案例的详细分析,深入了解控制流完整性保护技术在不同场景下的应用需求和面临的挑战,为提出针对性的保护策略提供实践依据。本研究还采用实验研究方法,搭建实验环境,对提出的控制流完整性保护策略进行验证。利用模拟攻击工具,对受保护的软件系统发起各种类型的控制流劫持攻击,如模拟Return-to-libc、ROP等攻击方式。通过对比分析在实施保护策略前后软件系统抵御攻击的能力,收集和分析实验数据,评估保护策略的有效性和性能影响。例如,记录攻击成功的次数、系统遭受攻击后的受损程度,以及保护策略实施后系统的性能指标变化,如运行时间、资源消耗等,以客观、准确地验证保护策略的实际效果。本研究的创新点主要体现在以下几个方面。从多维度分析体系结构支持的控制流完整性保护技术,不仅关注CFI技术本身的原理和实现方式,还深入探讨其在不同应用场景下的适用性,以及与硬件技术的协同作用,为该领域的研究提供了更全面、系统的视角。提出一种新的控制流完整性保护策略,该策略综合考虑了程序控制流图的精度优化、间接转移指令目标的精准识别以及硬件特性的充分利用。例如,采用新的分析技术提高程序控制流图的精度,有效解决现有静态分析方法导致间接调用分析结果不精确的问题;利用硬件保护机制确保关键信息的安全,增强保护策略的可靠性,在保障软件系统安全性的同时,降低性能开销,实现了安全性和性能的更好平衡。在实验研究中,创新性地设计实验方案,采用多样化的攻击模拟和全面的性能评估指标,更准确地评估保护策略的效果,为控制流完整性保护技术的实际应用提供了更具参考价值的实验数据和实践指导。二、控制流完整性保护技术基础2.1控制流完整性概念解析控制流完整性(CFI,ControlFlowIntegrity)作为保障软件系统安全的关键技术,在抵御控制流劫持攻击方面发挥着核心作用。其定义基于程序执行过程中控制流的合法性与预期性,旨在确保程序运行时的控制转移严格遵循预先定义的控制流图(CFG,Control-FlowGraph),从而有效防止攻击者利用软件漏洞恶意篡改控制流,实现对程序执行路径的非法操控。从原理层面深入剖析,CFI的工作机制紧密围绕程序的控制流图展开。控制流图是一种基于静态分析,以图的形式直观表达程序执行路径的工具,它以函数或基本块为节点,以控制转移关系为边,全面呈现了程序在正常执行过程中的各种可能路径。CFI技术着重关注控制流图中可能遭受攻击的间接转移指令,主要包括间接跳转(如JMPEBX,通过寄存器间接指定跳转地址)、间接调用(如通过函数指针进行函数调用)以及函数返回指令(ret,从栈中弹出返回地址以决定程序返回位置)。这些指令的目标地址在程序运行时动态确定,为攻击者实施控制流劫持攻击提供了可乘之机。CFI的核心实现方式是通过对程序控制流图的深度分析,精确获取间接转移指令目标的白名单。在程序运行过程中,每当执行到间接转移指令时,CFI机制会立即核对该指令的目标地址是否存在于预先构建的白名单中。若目标地址在白名单内,说明该控制转移符合程序的正常逻辑,允许指令正常执行,程序继续按照预期路径运行;反之,若目标地址不在白名单中,这就意味着控制流可能被劫持,存在恶意攻击行为,CFI机制将立即采取措施,如触发异常中断,阻止程序继续执行,从而有效防止攻击者利用控制流劫持实现恶意目的,保障软件系统的安全稳定运行。以一个简单的C语言程序为例,假设存在如下代码片段:#include<stdio.h>voidfunc1(){printf("Thisisfunc1\n");}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}voidfunc1(){printf("Thisisfunc1\n");}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}printf("Thisisfunc1\n");}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}}else{pFunc=func2;}pFunc();//间接函数调用return0;}pFunc=func2;}pFunc();//间接函数调用return0;}}pFunc();//间接函数调用return0;}pFunc();//间接函数调用return0;}return0;}}在这个程序中,pFunc()是一个间接函数调用,其目标函数在运行时根据用户输入动态确定。在CFI机制下,预先分析控制流图时,会将func1和func2的地址加入间接调用目标的白名单。当程序运行到pFunc()时,CFI会检查pFunc指向的地址是否在白名单内。若用户输入正常,pFunc指向func1或func2,地址在白名单中,调用正常进行;若攻击者通过漏洞篡改pFunc指向一个恶意函数地址,由于该地址不在白名单,CFI机制将检测到异常并阻止攻击,保护程序安全。2.2控制流劫持攻击分析在计算机安全领域,控制流劫持攻击是一种极具威胁性的攻击手段,它能够利用软件漏洞,恶意篡改程序的正常执行流程,使程序按照攻击者的意愿执行恶意操作,从而对系统安全造成严重破坏。随着技术的不断发展,控制流劫持攻击的方式也日益多样化和复杂化,其中ROP和JOP是两种较为典型且具有代表性的攻击方式。ROP(Return-OrientedProgramming,返回导向编程)攻击出现于2005年,是一种利用程序已存在的代码片段(常被称为gadget的短代码序列)来构造恶意执行代码的攻击方式。其攻击原理基于程序执行过程中对返回地址的处理机制。在正常程序执行时,当函数调用完成后,程序会从栈中弹出返回地址,然后将控制流转移到该地址继续执行。ROP攻击正是利用了这一机制,攻击者通过精心构造栈上的数据,将返回地址替换为一系列精心挑选的gadget的地址。这些gadget是程序中原本就存在的短代码片段,通常以ret指令结尾,攻击者将它们按照特定顺序连接起来,形成ROP链。当程序执行到ret指令时,会依次跳转到ROP链中的各个gadget执行,从而实现攻击者的恶意目的,如执行恶意代码、绕过访问控制、窃取敏感数据等。以一个简单的C语言程序为例,假设存在如下代码:#include<stdio.h>voidvulnerableFunction(){charbuffer[100];scanf("%s",buffer);}intmain(){vulnerableFunction();return0;}voidvulnerableFunction(){charbuffer[100];scanf("%s",buffer);}intmain(){vulnerableFunction();return0;}charbuffer[100];scanf("%s",buffer);}intmain(){vulnerableFunction();return0;}scanf("%s",buffer);}intmain(){vulnerableFunction();return0;}}intmain(){vulnerableFunction();return0;}intmain(){vulnerableFunction();return0;}vulnerableFunction();return0;}return0;}}在这个程序中,vulnerableFunction函数存在缓冲区溢出漏洞。攻击者可以通过输入超长字符串,覆盖栈上的返回地址,将其替换为ROP链的地址。ROP链中的gadget可能来自程序自身的代码,也可能来自链接的库函数。例如,攻击者可以找到一个gadget,它能够将特定参数传递给系统函数system,然后再找到一个gadget来触发system函数的调用,从而实现执行任意命令的目的,如执行system("rm-rf/")命令,对系统文件进行破坏。ROP攻击的危害是多方面的。从系统层面来看,它能够绕过许多常见的内存保护机制,如数据执行保护(DEP,DataExecutionPrevention)和栈随机化(ASLR,AddressSpaceLayoutRandomization)。DEP机制通过将数据所在内存页标识为不可执行,阻止攻击者注入并执行恶意代码,但ROP攻击利用已有的可执行代码片段,无需注入新代码,从而成功绕过DEP。ASLR机制通过在系统启动时随机化关键内存区域(如堆、栈、库等)的基地址,增加攻击者预测地址的难度,但ROP攻击利用程序中已有的代码片段,这些片段的地址在程序加载时就已确定,不受ASLR影响,使得攻击者能够准确地构建ROP链。从用户角度出发,ROP攻击可能导致用户敏感信息泄露,如账号密码、个人隐私数据等被窃取;还可能导致系统权限被提升,攻击者获取管理员或系统级别的权限,进而对系统进行全面控制,进行恶意操作,如安装恶意软件、篡改系统文件等。JOP(Jump-OrientedProgramming,面向跳转编程)攻击是另一种控制流劫持攻击方式,其原理与ROP攻击类似,但在实现方式上存在差异。JOP攻击主要利用程序中的跳转指令(如jmp、call等)来构建攻击逻辑,而不是像ROP那样依赖返回指令。攻击者通过寻找程序中以跳转指令结尾的代码片段(也可称为gadget),将这些gadget组合成JOP链,并通过篡改程序的控制流,使程序跳转到JOP链上执行。与ROP攻击不同,JOP攻击更注重对跳转目标的控制,通过精心构造跳转指令的目标地址,实现对程序执行流程的恶意操控。在实际攻击中,假设一个程序存在漏洞,攻击者可以利用该漏洞将程序的控制流转移到一个包含跳转指令的gadget上。例如,找到一个gadget,它包含jmpeax指令,攻击者通过控制eax寄存器的值,使其指向另一个恶意gadget的地址,从而实现程序控制流的恶意转移。如果攻击者能够成功构建JOP链,就可以在不依赖返回指令的情况下,实现对程序执行路径的劫持,执行一系列恶意操作,如篡改系统关键数据、破坏系统服务等。JOP攻击同样会对系统和用户造成严重危害。在系统层面,它破坏了程序的正常执行流程,导致系统运行出现异常,可能引发系统崩溃、服务中断等问题,影响系统的稳定性和可用性。对于用户而言,JOP攻击可能导致用户数据丢失或损坏,影响用户的正常使用;同时,攻击者还可能利用JOP攻击获取用户的敏感信息,侵犯用户的隐私和权益。除了ROP和JOP攻击外,还有其他一些控制流劫持攻击方式。例如,Return-to-libc攻击是一种早期的代码重用攻击方式,它利用程序中已加载的libc库中的函数,通过修改函数返回地址,使程序在返回时调用libc库中的特定函数,如system函数,从而实现执行任意命令的目的。这种攻击方式虽然相对较为简单,但在早期缺乏有效防御机制的情况下,也对系统安全构成了较大威胁。随着技术的发展,为了抵御控制流劫持攻击,各种防御机制应运而生,如控制流完整性(CFI)保护机制,它通过限制程序运行中的控制转移,确保其始终在原控制流图所限定的范围内,有效防范了这类攻击。2.3控制流完整性保护技术原理控制流完整性保护技术作为抵御控制流劫持攻击的关键防线,其工作原理基于对程序控制流图(CFG,Control-FlowGraph)的深度分析和对间接转移指令的严格监控。该技术的核心目标是确保程序在运行过程中的控制转移始终遵循预先定义的合法路径,从而有效防止攻击者利用漏洞篡改控制流,实现恶意目的。获取程序控制流图是控制流完整性保护技术的首要关键步骤。控制流图以图形化的方式直观呈现程序的执行流程,其中节点代表基本块(由一系列顺序执行且无跳转指令的指令序列组成)或函数,边则表示控制转移关系。获取控制流图的过程主要依赖静态分析技术,分析工具会对程序的二进制代码或源代码进行全面解析。对于源代码,编译器在编译阶段能够利用语法分析和语义分析等技术,精确识别程序中的各种控制结构,如条件语句(if-else、switch-case)、循环语句(for、while、do-while)以及函数调用等,进而构建出准确的控制流图。以如下简单C语言代码为例:#include<stdio.h>intmain(){inta=5;intb=3;if(a>b){printf("aisgreaterthanb\n");}else{printf("aislessthanorequaltob\n");}return0;}intmain(){inta=5;intb=3;if(a>b){printf("aisgreaterthanb\n");}else{printf("aislessthanorequaltob\n");}return0;}inta=5;intb=3;if(a>b){printf("aisgreaterthanb\n");}else{printf("aislessthanorequaltob\n");}return0;}intb=3;if(a>b){printf("aisgreaterthanb\n");}else{printf("aislessthanorequaltob\n");}return0;}if(a>b){printf("aisgreaterthanb\n");}else{printf("aislessthanorequaltob\n");}return0;}printf("aisgreaterthanb\n");}else{printf("aislessthanorequaltob\n");}return0;}}else{printf("aislessthanorequaltob\n");}return0;}printf("aislessthanorequaltob\n");}return0;}}return0;}return0;}}编译器在处理这段代码时,会将if-else结构视为控制流图中的一个分支点,分别生成对应两个分支的节点,并通过边连接表示程序可能的执行路径。对于二进制代码,反汇编工具会将其转换为汇编指令序列,然后通过分析指令间的跳转关系、函数调用约定等信息来构建控制流图。在实际应用中,由于程序的复杂性和动态特性,获取准确的控制流图并非易事。程序中可能存在复杂的数据结构和指针操作,这会增加分析的难度;一些动态链接库和运行时加载的代码,在静态分析阶段难以完全解析,可能导致控制流图信息缺失或不准确。在获取控制流图的基础上,精确识别间接转移指令并构建其目标白名单是控制流完整性保护技术的核心环节。间接转移指令包括间接跳转(如JMPEBX,通过寄存器间接指定跳转地址)、间接调用(如通过函数指针进行函数调用)以及函数返回指令(ret,从栈中弹出返回地址以决定程序返回位置)。这些指令的目标地址在程序运行时动态确定,为攻击者实施控制流劫持攻击提供了可乘之机。为了应对这一威胁,保护技术通过对控制流图的深入分析,确定这些间接转移指令在正常执行情况下可能的目标地址,并将其列入白名单。例如,对于一个包含函数指针调用的程序:#include<stdio.h>voidfunc1(){printf("Thisisfunc1\n");}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}voidfunc1(){printf("Thisisfunc1\n");}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}printf("Thisisfunc1\n");}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}}else{pFunc=func2;}pFunc();//间接函数调用return0;}pFunc=func2;}pFunc();//间接函数调用return0;}}pFunc();//间接函数调用return0;}pFunc();//间接函数调用return0;}return0;}}在分析控制流图时,工具会识别出pFunc()为间接函数调用指令,并将func1和func2的地址列入其目标白名单。构建白名单的过程需要综合考虑多种因素,包括程序的语义、函数的作用域和调用关系等。如果白名单构建不准确,可能会导致误判,将合法的目标地址排除在外,影响程序的正常运行;或者将非法目标地址纳入白名单,从而无法有效防御攻击。在程序运行阶段,实时核对间接转移指令的目标地址是否在白名单中是控制流完整性保护技术发挥作用的关键操作。当程序执行到间接转移指令时,保护机制会立即暂停指令的执行,提取指令的目标地址,并与预先构建的白名单进行比对。若目标地址在白名单内,说明该控制转移符合程序的正常逻辑,保护机制允许指令继续执行,程序按照预期路径推进;反之,若目标地址不在白名单中,这就意味着控制流可能被劫持,存在恶意攻击行为,保护机制将立即采取相应措施,如触发异常中断,阻止程序继续执行,防止攻击者利用非法控制流执行恶意代码。在实际运行环境中,为了确保核对过程的高效性和准确性,通常会采用硬件辅助或优化的算法来加速白名单的查找和比对操作。一些硬件架构提供了专门的寄存器或指令来支持快速的地址比较,从而降低核对过程对系统性能的影响。同时,为了应对动态变化的程序执行环境,白名单可能需要在运行时进行动态更新和维护,以确保其始终与程序的实际执行情况相符。三、体系结构对控制流完整性保护的支持3.1常见体系结构概述在当今的计算机领域,多种体系结构共同构建起计算世界的基础,其中x86、ARM和RISC-V凭借各自独特的特性,在不同应用场景中发挥着关键作用,为控制流完整性保护技术的研究与应用提供了多样化的硬件平台。x86架构作为计算机领域应用最为广泛的指令集架构之一,具有深厚的历史底蕴和广泛的市场应用。它起源于英特尔公司1978年发布的8086CPU,历经多年的发展与演进,成为现代计算机体系结构的重要基石。x86架构基于复杂指令集计算机(CISC,ComplexInstructionSetComputer)设计理念,指令集丰富且复杂。这使得x86架构的CPU能够执行多种复杂操作,具备强大的处理能力,可支持高频率、多核的CPU配置,在桌面电脑和服务器领域表现出色,能够满足用户对于高性能计算和多任务处理的需求。x86架构还具有高度的可编程性,开发者可以通过编写汇编语言或高级编程语言来充分发挥其性能优势。向下兼容是x86架构的一大显著特点,较新版本的CPU能够运行较旧版本的软件,这极大地保护了用户的软件投资,促进了软件生态系统的稳定发展。但丰富的指令集也导致了x86架构在执行某些简单操作时,可能存在性能损耗,并且其复杂的设计使得芯片制造难度和成本相对较高。在授权模式方面,x86架构属于封闭源代码的指令集架构,主要授权厂商为Intel和AMD,其他公司和组织若要使用x86架构,需通过授权和许可,涵盖CPUIP核授权、芯片许可和软件许可等方面,并遵守相关的授权条款和使用规定。ARM架构以其独特的优势在移动设备、嵌入式系统和物联网等领域占据重要地位。它由英国ARM公司设计,是一种精简指令集(RISC,ReducedInstructionSetComputing)的CPU架构。ARM架构采用精简指令集设计,每个指令简单且执行速度快,具有高效率和低功耗的显著特点,非常适合对功耗要求严格的移动设备和嵌入式系统。在硬件和软件之间引入了多个抽象层次,ARM架构使得软件开发更加灵活,具备更高的可移植性,方便开发者在不同的硬件平台上进行应用开发。ARM架构还拥有丰富的扩展,如Thumb-2、NEON和TrustZone等。Thumb-2技术在16位和32位指令集之间实现了高效切换,提升了代码密度和执行效率;NEON扩展提供了强大的多媒体和信号处理能力,满足了移动设备对视频、音频处理等多媒体应用的需求;TrustZone技术则提供了硬件级别的安全保护,能够有效保护敏感数据,防范恶意攻击,增强了系统的安全性。ARM架构的实现具有很高的灵活性,芯片制造商可以根据自身需求选择不同的封装形式、核数量和频率等,以满足多样化的市场需求。随着物联网和移动设备市场的持续增长,对能效比的追求使得ARM架构在未来仍将发挥重要作用。RISC-V架构是一个新兴的开源指令集架构,起源于加州大学伯克利分校的项目,近年来在计算机领域崭露头角,展现出巨大的发展潜力。RISC-V指令集完全开源,这一特性赋予了用户极大的自由度,用户可以根据自身需求定制、扩展和设计自己的处理器,为创新和个性化应用开发提供了广阔空间。其设计理念强调简洁性和高效性,指令集设计简单,易于移植Unix系统,降低了开发难度和成本。RISC-V架构采用模块化设计,用户可以根据具体应用场景灵活选择适合的指令集模块,这种灵活性使得RISC-V架构能够广泛应用于从嵌入式系统到高性能计算等多个领域。目前,RISC-V架构已经得到了众多芯片公司的认可,在智能穿戴产品、物联网设备、服务器CPU等领域都有应用案例,其开源生态系统也在快速发展,吸引了越来越多的开发者和企业参与其中,推动着RISC-V技术不断进步。3.2体系结构层面的支持机制不同的体系结构在硬件层面为控制流完整性保护提供了各具特色的支持机制,这些机制借助特殊指令、寄存器等硬件资源,从不同角度增强了控制流完整性保护的能力,为软件系统的安全运行奠定了坚实基础。x86架构在控制流完整性保护方面展现出独特的硬件支持特性。在特殊指令方面,Intel自第11代酷睿处理器(RocketLake)开始引入的控制流强制技术(CET,Control-flowEnforcementTechnology)具有重要意义。CET主要包含间接分支跟踪(IBT,IndirectBranchTracking)和影子栈(ShadowStack)两大关键功能。IBT通过在硬件层面上对每个间接分支进行严格检查,确保跳转目标地址的合法性,从而有效防止攻击者通过修改程序中的间接分支(如函数指针、虚函数表指针等)来控制程序的执行路径。当程序执行到间接跳转指令时,IBT会立即核对该指令的目标地址是否位于特定的、经过验证的合法区域内,若目标地址不合法,程序将触发异常,阻止恶意的控制流劫持行为。影子栈则是一个独立于程序栈的硬件保护机制,专门用于保存返回地址。在正常的函数调用过程中,程序不仅会将返回地址压入常规栈中,同时也会将其保存到影子栈中。当函数返回时,CET会对比栈上的返回地址与影子栈中的返回地址,若两者不一致,说明返回地址可能被篡改,程序会立即触发异常,有效防范了栈溢出攻击导致的控制流劫持。除了CET技术,x86架构还提供了一些其他相关的指令和功能来辅助控制流完整性保护。如英特尔的内存保护密钥(MPK,MemoryProtectionKeys)技术,它允许将内存划分为多个区域,并为每个区域分配不同的保护密钥。在控制流完整性保护中,MPK可以用于限制对特定内存区域的访问,确保控制流相关的数据(如函数指针、返回地址等)存储在受保护的内存区域内,防止被非法篡改。假设一个程序中有一个关键的函数指针,其指向的函数用于执行重要的系统操作。通过MPK技术,可以将存储该函数指针的内存区域设置为特定的保护密钥,只有具有相应密钥权限的代码才能对其进行访问和修改。这样一来,攻击者如果试图通过缓冲区溢出等漏洞篡改该函数指针,由于缺乏正确的密钥权限,将无法成功修改,从而保障了控制流的完整性。在寄存器支持方面,x86架构拥有丰富的通用寄存器和专用寄存器,这些寄存器在控制流完整性保护中发挥着重要作用。通用寄存器(如EAX、EBX、ECX等)可以用于存储和传递控制流相关的信息,如函数参数、返回值等。在函数调用过程中,通过将函数参数传递到相应的寄存器中,可以确保参数的准确性和完整性,防止攻击者通过篡改参数来影响控制流。专用寄存器(如EIP,指令指针寄存器)则直接控制程序的执行流程。EIP始终指向当前即将执行的指令地址,在控制流完整性保护中,对EIP的保护至关重要。一些硬件保护机制会监控EIP的变化,确保其跳转地址符合程序的正常控制流逻辑,防止攻击者通过非法修改EIP来劫持控制流。ARM架构在控制流完整性保护的硬件支持方面也有其独特之处。ARM引入了分支目标识别(BTI,BranchTargetIdentification)技术,这是一种针对间接分支指令的硬件防护机制。BTI通过在程序执行间接分支指令时,对目标地址进行检查,确保其是合法的分支目标,从而有效抵御控制流劫持攻击。其工作原理是在编译阶段,编译器会为每个合法的分支目标添加特定的标识信息。当程序运行到间接分支指令时,硬件会根据这些标识信息来验证目标地址的合法性。如果目标地址没有正确的标识,说明该分支可能是非法的,硬件将触发异常,阻止控制流的非法转移。在寄存器利用方面,ARM架构的寄存器设计也为控制流完整性保护提供了支持。ARM处理器拥有多个通用寄存器和特殊功能寄存器。通用寄存器可以用于存储控制流相关的数据,如函数调用的参数和返回值等。在函数调用过程中,通过寄存器传递参数和返回值,可以减少对内存的访问,提高程序的执行效率,同时也降低了攻击者通过篡改内存数据来影响控制流的风险。特殊功能寄存器(如PC,程序计数器)在控制流中起着核心作用。PC始终指向当前正在执行的指令的下一条指令地址,类似于x86架构中的EIP。ARM架构通过对PC的严格管理和保护,确保程序按照预期的控制流顺序执行。当发生函数调用或跳转时,PC的值会根据合法的控制流逻辑进行更新,硬件会对PC的更新过程进行监控,防止非法的PC值修改导致控制流劫持。RISC-V架构作为新兴的开源指令集架构,在控制流完整性保护的硬件支持方面展现出创新性和灵活性。由于其开源特性,RISC-V架构允许用户根据自身需求定制和扩展硬件支持机制,为控制流完整性保护提供了多样化的实现方式。一些基于RISC-V架构的设计引入了类似影子栈的机制来保护返回地址。通过在硬件中实现一个独立的影子栈,在函数调用时将返回地址同时保存到常规栈和影子栈中。当函数返回时,对比两个栈中的返回地址,若不一致则触发异常,有效防范了栈溢出攻击对控制流的劫持。RISC-V架构还可以通过扩展指令集来实现对控制流完整性的增强保护。用户可以定义新的指令,用于对间接转移指令的目标地址进行验证和检查。在程序执行间接跳转或间接调用指令时,新指令可以根据预先设定的规则,对目标地址进行合法性验证。若目标地址不符合规则,说明可能存在控制流劫持风险,程序将采取相应的防御措施,如触发异常或终止执行。3.3体系结构支持的优势与局限体系结构对控制流完整性保护的支持在增强软件安全性方面展现出诸多显著优势,但也不可避免地存在一些局限性,这些特性对于深入理解和有效应用该技术至关重要。从优势角度来看,体系结构支持的控制流完整性保护在性能提升方面成效显著。以x86架构中的CET技术为例,其间接分支跟踪(IBT)和影子栈功能在硬件层面直接对控制流进行监控和保护,避免了大量复杂的软件检查操作。在面对频繁的间接分支指令时,硬件能够快速准确地验证跳转目标地址的合法性,相比单纯依赖软件实现的控制流完整性保护,大大减少了指令执行的额外开销,显著提升了程序的运行效率。据相关研究表明,在一些对性能要求较高的应用场景中,采用CET技术后,程序的运行速度提升了15%-25%,有效保障了系统在高性能需求下的稳定运行。这种保护方式还具有极高的安全性和可靠性。硬件层面的支持为控制流完整性提供了坚实的保障,使得攻击者难以绕过硬件机制进行控制流劫持攻击。如ARM架构的分支目标识别(BTI)技术,通过在编译阶段为合法分支目标添加特定标识,在运行时硬件对间接分支指令的目标地址进行严格验证。这种方式能够有效抵御各类复杂的控制流劫持攻击,包括ROP和JOP攻击等,极大地增强了系统的安全性。由于硬件机制的稳定性和可靠性,相比软件层面的保护措施,硬件支持的控制流完整性保护在长期运行过程中,能够更稳定地发挥作用,减少因软件漏洞或错误导致的安全风险,为系统的可靠运行提供了有力支撑。兼容性和可扩展性也是体系结构支持的重要优势。在兼容性方面,现代体系结构在设计时充分考虑了对现有软件生态系统的支持,确保在引入控制流完整性保护机制后,不会对大量已有的应用程序造成兼容性问题。例如,x86架构在引入CET技术后,通过与操作系统和编译器的协同工作,使得大部分现有的x86应用程序能够在开启CET保护的环境下正常运行,无需进行大规模的代码修改。在可扩展性方面,一些体系结构如RISC-V,因其开源和灵活的特性,允许用户根据自身需求对控制流完整性保护机制进行定制和扩展。用户可以根据特定的应用场景,添加新的硬件指令或功能模块,进一步增强控制流完整性保护的能力,满足不断变化的安全需求。然而,体系结构支持的控制流完整性保护也存在一定的局限性。硬件成本的增加是一个不容忽视的问题。为了实现对控制流完整性的硬件支持,芯片制造商需要在芯片设计和制造过程中增加额外的硬件资源和电路设计。例如,实现影子栈功能需要额外的寄存器和存储单元来保存返回地址,这无疑会增加芯片的面积和制造成本。对于一些对成本敏感的应用领域,如低端嵌入式设备和物联网终端,过高的硬件成本可能会限制控制流完整性保护技术的广泛应用。兼容性问题在实际应用中也时有出现。尽管体系结构在设计时尽力保障兼容性,但在复杂的软件环境下,仍可能存在部分软件与控制流完整性保护机制不兼容的情况。一些老旧的应用程序,由于其开发时未考虑到新的硬件保护机制,可能在运行时出现异常或错误。在某些特定的工业控制系统中,部分运行多年的软件可能无法适应x86架构中CET技术的要求,导致在升级系统以启用CET保护时,出现软件无法正常运行的问题,需要耗费大量的时间和精力进行适配和调试。性能开销虽然相比软件实现有了显著降低,但在某些极端情况下仍可能对系统性能产生一定影响。对于一些高度依赖间接分支指令且对性能要求苛刻的应用程序,如某些实时性要求极高的多媒体处理软件和金融交易系统,硬件保护机制在验证控制流时,仍会占用一定的处理器资源和时间,导致程序的响应速度略有下降。在一些对帧率要求极高的游戏应用中,开启控制流完整性保护后,可能会出现轻微的卡顿现象,影响用户体验。四、体系结构支持的控制流完整性保护技术实现4.1基于硬件辅助的实现方案在体系结构支持的控制流完整性保护技术实现中,基于硬件辅助的方案展现出独特的优势,其中RISC-V架构中利用协处理器验证控制流完整性的设计颇具代表性。在RISC-V架构的硬件辅助实现方案中,协处理器的设计是核心要素之一。协处理器被专门设计用于承担控制流完整性验证的关键任务。其内部结构包含多个功能模块,其中地址验证模块负责对接收到的间接转移指令的目标地址进行合法性验证。该模块预先存储了通过静态分析程序控制流图所获取的合法目标地址范围或白名单。当接收到目标地址时,地址验证模块会迅速将其与白名单进行比对,判断该地址是否在合法范围内。寄存器管理模块则负责管理与控制流完整性验证相关的寄存器,这些寄存器用于存储临时数据、验证状态等信息,确保验证过程的高效进行。例如,其中一个寄存器专门用于记录当前正在验证的间接转移指令的类型(是间接跳转、间接调用还是函数返回指令),以便地址验证模块根据不同指令类型进行更精准的验证。在工作流程方面,当程序执行到间接转移指令时,处理器会首先暂停指令的执行,并将相关信息传递给协处理器。具体来说,处理器会将间接转移指令的目标地址、指令类型以及当前程序的执行上下文等信息发送给协处理器。协处理器接收到这些信息后,地址验证模块立即开始工作,将目标地址与预先存储的白名单进行比对。如果目标地址在白名单内,说明该控制转移符合程序的正常逻辑,协处理器会向处理器发送允许执行的信号,处理器继续执行该间接转移指令,程序按照预期路径推进。若目标地址不在白名单中,协处理器会判定控制流可能被劫持,存在恶意攻击行为,此时协处理器会触发异常信号。处理器接收到异常信号后,会立即采取相应措施,如终止程序执行,或者将程序转移到安全处理流程,以防止攻击者利用非法控制流执行恶意代码。为了更清晰地理解这一过程,我们以一个简单的函数调用场景为例。假设在RISC-V架构的程序中,存在一个函数指针pFunc,用于指向不同的函数。在正常情况下,pFunc只会指向预先定义好的几个合法函数,这些函数的地址被存储在协处理器的白名单中。当程序执行到通过pFunc进行函数调用的间接转移指令时,处理器将pFunc所指向的目标地址以及指令类型(间接调用)等信息发送给协处理器。协处理器的地址验证模块将该目标地址与白名单进行比对,如果目标地址是白名单中合法函数的地址,协处理器允许调用继续进行,程序正常执行被调用函数的代码。但如果攻击者通过漏洞篡改了pFunc,使其指向一个恶意函数的地址,由于该地址不在白名单中,协处理器会检测到异常并触发异常信号,从而阻止攻击的发生。硬件辅助实现方案在性能提升方面具有显著效果。由于协处理器专门负责控制流完整性验证,减轻了主处理器的负担,使得主处理器能够专注于程序的正常执行,提高了整体系统的运行效率。实验数据表明,相比纯软件实现的控制流完整性保护方案,基于硬件辅助的方案在处理大量间接转移指令时,程序的执行速度提升了20%-30%。该方案在安全性方面也表现出色,硬件级别的验证机制使得攻击者难以绕过验证进行控制流劫持攻击,大大增强了系统的安全性。然而,这种方案也存在一定的局限性,如硬件成本的增加,需要额外的硬件资源来实现协处理器;在兼容性方面,对于一些不支持该硬件辅助功能的软件或系统,可能需要进行额外的适配工作。4.2结合软件技术的协同实现将硬件支持与软件技术相结合,是实现更高效控制流完整性保护的关键路径。编译器插桩作为一种重要的软件技术,与硬件支持协同工作,能够充分发挥两者的优势,提升控制流完整性保护的整体效能。编译器插桩技术在控制流完整性保护中发挥着不可或缺的作用。其基本原理是在程序编译阶段,由编译器在程序代码中插入特定的代码片段,这些插桩代码就如同在程序中埋下的“探测器”,用于监控和保护程序的控制流。在间接转移指令(如间接跳转、间接调用、函数返回指令)处插入验证代码,是编译器插桩的核心操作之一。以间接调用指令为例,假设在一个C语言程序中有如下代码:#include<stdio.h>voidfunc1(){printf("Thisisfunc1\n");}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}voidfunc1(){printf("Thisisfunc1\n");}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}printf("Thisisfunc1\n");}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}voidfunc2(){printf("Thisisfunc2\n");}intmain(){void(*pFunc)();intchoice;printf("Enter1tocallfunc1,2tocallfunc2:");scanf("%d",&choice);if(choice==1){pFunc=func1;}else{pFunc=func2;}pFunc();//间接函数调用return0;}printf("Thisisfunc2\n");}int

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论