CN111381879B - 一种数据处理方法及装置 - Google Patents
一种数据处理方法及装置 Download PDFInfo
- Publication number
- CN111381879B CN111381879B CN201811651849.XA CN201811651849A CN111381879B CN 111381879 B CN111381879 B CN 111381879B CN 201811651849 A CN201811651849 A CN 201811651849A CN 111381879 B CN111381879 B CN 111381879B
- Authority
- CN
- China
- Prior art keywords
- value
- function
- program
- register
- variable
- Prior art date
- Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
- Active
Links
Images
Classifications
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F9/00—Arrangements for program control, e.g. control units
- G06F9/06—Arrangements for program control, e.g. control units using stored programs, i.e. using an internal store of processing equipment to receive or retain programs
- G06F9/30—Arrangements for executing machine instructions, e.g. instruction decode
- G06F9/30098—Register arrangements
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F9/00—Arrangements for program control, e.g. control units
- G06F9/06—Arrangements for program control, e.g. control units using stored programs, i.e. using an internal store of processing equipment to receive or retain programs
- G06F9/30—Arrangements for executing machine instructions, e.g. instruction decode
- G06F9/30003—Arrangements for executing specific machine instructions
- G06F9/3005—Arrangements for executing specific machine instructions to perform operations for flow control
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F9/00—Arrangements for program control, e.g. control units
- G06F9/06—Arrangements for program control, e.g. control units using stored programs, i.e. using an internal store of processing equipment to receive or retain programs
- G06F9/30—Arrangements for executing machine instructions, e.g. instruction decode
- G06F9/30003—Arrangements for executing specific machine instructions
- G06F9/3005—Arrangements for executing specific machine instructions to perform operations for flow control
- G06F9/30069—Instruction skipping instructions, e.g. SKIP
Landscapes
- Engineering & Computer Science (AREA)
- Software Systems (AREA)
- Theoretical Computer Science (AREA)
- Physics & Mathematics (AREA)
- General Engineering & Computer Science (AREA)
- General Physics & Mathematics (AREA)
- Storage Device Security (AREA)
Abstract
本申请公开了一种数据处理方法及装置,属于计算机技术领域,用于提高程序的安全性,在该方法中,在需要读取程序中的关键变量的值时,从安全存储区域中确定与所述关键变量对应的值标识,其中,所述关键变量为能够影响所述程序的执行逻辑的程序变量,所述值标识是根据所述关键变量的赋值确定的,所述安全存储区域包括处理器中的寄存器和内核内存区域,所述内核内存区域具有对内核可读可写且对应用只读的属性;从所述安全存储区域中读取所述值标识,并确定与所述值标识对应的值;将所述值标识对应的值确定为所述关键变量的值。
Description
技术领域
本申请涉及计算机技术领域,尤其涉及一种数据处理方法及装置。
背景技术
程序在执行过程中容易受到黑客或者恶意用户发起的攻击,使得程序中的一些数据被篡改或被窃取,从而导致一些程序安全问题。例如,一种比较惯用的攻击手段是控制流劫持攻击,控制流劫持攻击的典型代表有面向返回的编程(return orientedprogramming,ROP)、面向跳跃的编程(jump oriented Programming,JOP)等方式,通过控制流劫持攻击,攻击者可以劫持程序的控制流去执行非预期的恶意代码逻辑,进而破坏或者窃取程序数据,实现提权等攻击目的。另外一些攻击方式例如还可以在程序运行时篡改程序的配置数据、认证标志等数据变量,这样也可以实现认证绕过和提权等攻击目的,从而导致程序安全问题。
可见,目前的程序存在安全攻击的问题,程序的安全性较差。
发明内容
本申请实施例提供一种数据处理方法及装置,用于提高程序的安全性。
第一方面,提供一种数据处理方法,该方法可以应用于计算设备中,该计算设备可以运行程序,该计算设备包括处理器,该处理器中包括有寄存器。在该方法中,在需要读取程序中的关键变量的值时,可以从安全存储区域中确定与该关键变量对应的值标识,再从该安全存储区域中读取该值标识,并确定与该值标识对应的值,再将确定出的与该值标识对应的值确定为该关键变量的值,进而还可以使用该关键变量的值,其中,该关键变量为能够影响程序的执行逻辑的程序变量,值标识是根据关键变量的赋值确定的,该安全存储区域包括处理器中的寄存器和内核内存区域,该内核内存区域具有对内核可读可写且对应用只读的属性。
在上述技术方案中,关键变量是指能够影响程序的执行逻辑的程序变量,可以这样理解,关键变量的值被篡改之后,如果使用篡改后的关键变量的值运行程序的话则可能引起程序的执行逻辑发生变化,即可能执行一些非预期的恶意代码逻辑,进而使得程序陷入安全问题。关键变量可以是指代码指针或重要数据变量,无论是代码指针还是重要数据变量的篡改都可能造成程序执行逻辑的变化,例如,代码指针的篡改可能会引起控制流劫持攻击,而重要数据变量的篡改可能造成数据攻击,也就是说,该代码指针是能够引起程序执行逻辑发生变化的指针。
在一种可能的设计中,代码指针可以包括函数调用返回地址、函数指针变量、C++对象虚函数表指针、代码位置指针以及其它能够引起程序执行逻辑发生变化的指针。
在一种可能的设计中,重要数据变量可以包括程序中的函数参数或全局变量或其它能够引起程序执行逻辑发生变化的程序变量。
上述技术方案中的值标识,是指能够表示关键变量的值的标识信息,但值标识与其对应的值不同,在一种可能的设计中,可以对关键变量的值按照一定的规则进行编码处理,进而将编码处理后得到编码信息作为该关键变量的值对应的值标识,一个关键变量的值与其对应的值标识,长度可以相等,或者也可以不等,或者可以这样理解,关键变量的值的长度可以大于对应的值标识的长度,或者也可以小于对应的值标识的长度,或者也可以等于对应的值标识的长度,对于一个关键变量的值对应的值标识的长度,具体可以根据该关键变量对应的值标识转换规则决定。其中,关键变量的值的长度以及值标识的长度,可以是指二进制长度,即关键变量的值或者关键变量的值对应的值标识所包括的二进制的位数。
在上述技术方案中,值标识是保存在安全存储区域中,该安全存储区域包括处理器中的寄存器和内核内存区域,该内核内存区域具有对内核可读可写但对应用只读的属性。具体来说,是将值标识直接存储在寄存器中,当寄存器存满时,则可以将先前存储的值标识溢出到内核内存区域,由于内核内存区域对内核可读可写但对应用只读,这样可以防止攻击者对值标识进行篡改,进而确保值标识的安全。
选择处理器中的寄存器保护程序中的关键变量的值标识,是基于以下考虑。第一,攻击者利用程序内存安全漏洞无法篡改寄存器的值,因为寄存器都位于处理器之内,与内存分离,并且每个寄存器都只能通过寄存器的名字才能访问。第二,寄存器位于处理器之内,处理器访问寄存器的速度比访问内存的速度高很多,这可以在一定程度上优化本方案的性能。第三,使用寄存器保护程序中的关键变量的值标识,而寄存器都是公开且只能通过寄存器的名字访问,这样攻击者无法泄露或者篡改寄存器的值,从而可以抵抗信息泄露攻击。
在上述技术方案中,通过将关键变量的值转换成值标识并保存在安全存储区域中的方式,当需要读取某个关键变量的值时,则可以从安全存储区域中去读取对应的值标识,然后在确定出与读取到的值标识对应的值,并将最终确定出的值作为该关键变量的值进行使用,这样可以减小关键变量的值被篡改的可能,尽量避免程序的执行逻辑被篡改,从而提高程序的安全。通过该方案,不仅可以防止攻击者通过篡改代码指针而进行的控制流劫持攻击,还可以防御攻击者篡改程序的重要数据变量进行的数据攻击,以实现针对控制流劫持攻击和数据攻击同时进行防御的统一、有效的程序保护方式,提高程序的安全性。
在一种可能的设计中,在从安全存储区域中确定与每个关键变量对应的值标识之前,需要将该关键变量对应的值标识存储在该安全存储区域中,所以,在从安全存储区域中确定需要读取的程序中的某个关键变量的值标识之前,可以在关键变量被赋值之前或者之后,即获得该关键变量的赋值,并通过与该关键变量的变量类型对应的转换规则对该关键变量的赋值进行转换处理,以得到与该赋值对应的值标识,并将得到的值标识存储到寄存器中。
在上述技术方案中,针对不同变量类型的关键变量,可以设置一种对应的值标识转换规则,进而,当需要将某个关键变量的值转换成对应的值标识进行安全保存时,则可以采用与该关键变量的变量类型对应的值标识的转换规则进行值标识的转换处理,进而得到相应的值标识。通过关键变量的变量类型匹配的值标识转换规则,可以将各种关键变量的变量类型的特点考虑在内,这样在进行值标识转换时的针对性更强,可以在一定长度上提高转换效率,并且可以便于后续从安全存储区域中进行值标识的检索与查找,以提高后续进行值标识查找的效率。
在一种可能的设计中,在将值标识存储到寄存器中时,可以先确定该寄存器的空闲位是否能够存储当前需要存储的值标识,若确定寄存器的空闲位不能够存储该值标识,则将该寄存器中存储的其它值标识溢出到内核内存区域,并将当前需要的存储的值标识再存储到溢出其它值标识后的寄存器中。
在上述技术方案中,考虑到寄存器的存储容量,所以在寄存器存满时可以采用溢出到内核内存区域中的方式来增加值标识的可存储空间,以便于存储更多的值标识。其中的内核内存区域可以是内存中的一块存储区域,但是该内核内存区域具有针对内核可读可写但对应用只读的特性,所以攻击者是难以篡改溢出到该内核内存区域中的值标识的,以确保值标识的安全,进而提升程序的安全性。
在一种可能的设计中,在得到与关键变量的赋值对应的值标识之后,还可以将该赋值、该值标识、该赋值与该值标识之间的对应关系存储到转换表中。
在上述技术方案中,可以将关键变量的值与转换得到的值标识之间对应的关系存储到转换表中,即可以通过一个转换表来记录关键变量的值与其对应的值标识之间的对应关系,进而可以通过该转换表确定出某个值标识对应的关键变量实际的赋值,从而可以提高依据值标识确定对应的关键变脸过的值的确定效率。
在一种可能的设计中,在关键变量为函数调用返回地址时,通过与该关键变量的变量类型对应的转换规则对该关键变量的赋值进行转换处理,以得到与该赋值对应的值标识,可以包括:将被调用函数的函数调用位置确定为该被调用函数的函数调用返回地址,进而再根据该被调用函数的调用方式对应的值标识分配方式,为每个函数调用位置分配对应的一个值标识,以得到每个函数调用位置对应的值标识,其中,该关键变量当前的函数调用位置对应的值标识则为其赋值对应的值标识。
在上述技术方案中,由于一个程序中所有可能的函数调用返回地址集合是有限的,它等同于程序中所有的函数调用位置,所以对程序中函数调用位置分配值标识就相当于实现了对函数调用返回地址分配值标识,所以可以编译过程中通过扫描程序中所有的函数调用的位置一确定所有可能的函数调用返回地址,并为每个函数调用位置分配一个不同的值标识,进而,则可以得到每个函数调用返回地址对应的值标识。通过巧妙的利用函数调用位置与函数调用返回地址之间的对应关系,并且基于函数调用位置预先可知的特性,进而可以为每个函数调用位置分配一个不同的值标识,进而可以针对函数调用返回地址这种关键变量,提供了一种快速得到一个程序中每个被调用函数的每个函数调用返回地址的值标识,可以在一定程度上提高值标识的分配效率。另外,还可以根据函数调用方式的不同采用对应不同的值标识分配方式,可以结合各种函数调用方式的调用特点进行对应的值标识的分配,针对性更强,同时针对不同函数调用方式的函数的值标识的分配方式更加灵活。
在一种可能的设计中,在被调用函数的调用方式为直接函数调用时,可以确定该被调用函数的所有函数调用位置,并根据所有函数调用位置的数量确定用于表示值标识的字段长度,确定的值标识的字段长度能够表示所述所有函数调用位置的数量,再通过确定的值标识的字段长度,用不同的二进制字段标识不同的函数调用位置,进而获得直接函数调用的每个函数调用位置的值标识。
在上述技术方案中,由于直接函数调用的函数的所有函数调用位置是预先可知的,且针对一个函数来说,其被直接调用的所有函数调用位置的数量一般也不会很多,基于直接函数调用的该调用特点,在该技术方案中可以根据直接函数调用的所有函数调用位置的数量来确定用于表示值标识的二进制字段的长度,以使得确定出的二进制字段的长度能够完全表示所有的直接函数调用的函数调用位置,并且又尽量减少浪费,例如,当直接函数调用的所有函数调用位置的数量为14个,那么则可以采用4位的二进制字段长度来进行该函数的值标识的表示,例如,4位的二进制长度最多能表示16个(即24)不同的值标识,例如以0011表示00101101的函数调用返回地址,以1011再表示10110011的函数调用返回地址,等等。采用该种值标识的表示方式,值标识的长度一般比函数调用返回地址的长度短,所以这样可以在一定程度上降低值标识的数据量,这样可以使用寄存器存储更多数量的值标识,以尽量减少寄存器的溢出。
在一种可能的设计中,程序中的每个直接函数调用的被调用函数对应配置一个寄存器以存储该被调用函数的值标识。
在一种可能的设计中,在被调用函数的调用方式为间接函数调用或者外部函数调用时,可以通过预定字段长度的值标识方式,用不同的二进制字段表示不同的函数调用位置,进而获得每个函数调用位置的值标识。
在上述技术方案中,针对间接函数调用或者外部函数调用的函数,由于其函数调用情况比较复杂,导致函数调用位置出现的可能较多,所以可以直接采用预定字段长度的值标识方式来实现值标识的分配,例如可以采用16位的二进制字段来标识间接函数调用或者外部函数调用的函数的多个函数调用位置,这样可以得到216个不同的值标识可能,以满足间接函数调用或者外部函数调用的较多数量的函数调用返回地址的对应的值标识,确保各个函数调用返回地址的值标识的不重复表示,提高值标识的表示准确性。
在一种可能的设计中,程序中的所有间接函数调用或者外部函数调用的被调用函数配置一个寄存器组,以通过配置的寄存器组存储所有被调用函数的值标识,而寄存器组可以包括一个或者多个寄存器。
在上述技术方案中,考虑到间接函数调用或者外部函数调用的函数调用返回地址的数量较多,为了使得所有不同的函数调用返回地址能够准确唯一的表示,在前述采用预定字段长的二进制表示方式上,由于一个寄存器的位数一般是有效的,所以还可以通过多个寄存器来存储这些较多数量的函数调用返回地址,这样可以尽量减少寄存器的溢出。
在一种可能的设计中,在关键变量为函数指针变量或C++对象虚函数表指针或代码位置指针或重要数据变量时,可以根据关键变量的内存地址和值来确定该关键变量的赋值对应的值标识。
在上述技术方案中,例如,关键变量的内存地址为&fptr,值为fun1(*fptr),则可以采用(&fptr,fun1)作为其值标识的方式来进行值标识的表示,由于每个指针变量均具有内存地址和值这两个属性,所以采用该种方式可以统一这种同时具有内存地址和值这两种属性的关键变量的值标识的表示方式,并且这样的值标识的表示方式简单,计算量较小,可以在一定程度上提高值标识的确定效率。
在一种可能的设计中,针对上述以内存地址和值进行值标识的表示的关键变量,在从安全存储区域中确定与关键变量对应的值标识时,可以关键变量的内存地址作为关键字从安全内存区域中进行检索,并将检索到的包括该内存地址的值标识确定为该关键变量对应的值标识。
在上述技术方案中,以内存地址作为关键字进行值标识的快速检索和查找,可以确保较快的检索效率,进而提高从安全存储区域中存储的多个值标识中确定出需要对应的值标识的确定效率,进而提高程序的运行效率。
在一种可能的设计中,在关键变量为函数指针变量或代码位置指针,在以关键变量的内存地址作为关键字从安全存储区域中进行检索时,可以先以内存地址作为关键字在安全存储区域中的内核内存区域中进行检索,以判断在内核内存区域在是否有包括该内存地址的值标识,若有,则将包括该内存地址的值标识确定为该关键变量对应的值标识,若没有,则再以该内存地址作为关键字在安全存储区域中的寄存器中进行检索,以将寄存器中包括该内存地址的值标识确定为该关键变量对应的值标识。
在上述技术方案中,针对函数指针变量或代码位置指针这两种关键变量,提供了以内存地址从内核内存区域中进行快速检索的快读路径,即从内核内存区域快速查找值标识的方式,这是考虑到程序中的函数指针变量或代码位置指针的读操作出现频率通常要比其写操作频繁的多,所以为了提高性能,以便尽量缩短检索值标识的检索时长,所以可以通过该快读路径从内核内存区域中进行优先检索,进而提高值标识的查找效率。
在一种可能的设计中,对于针对函数指针变量或代码位置指针这两种关键变量提出的快读路径进行快速检索的方案中,当以内存地址作为关键字从内核内存区域中确定出该内核内存区域有包括该内存地址的值标识,进一步地,可以在判断该值标识所包括的值与该关键变量当前在程序中实际的值是否相同,若相同,才将内核内存区域中确定出的包括该内存地址的值标识确定为该关键变量对应的值标识,若不相同,则不将内核内存区域中确定出的包括该内存地址的值标识确定为该关键变量对应的值标识,并再在寄存器中以该内存地址作为关键字进行检索,以将从寄存器中检索到的包括该内存地址的值标识确定为该关键变量对应的值标识。
在上述技术方案中,由于一个关键变量的值标识在被从寄存器溢出到内核内存区域之后,该关键变量的值又被修改(或者理解为重新赋值)从而使得新赋值的值标识又被存到了寄存器中,所以此时在进行值标识检索时,内核内存区域中的值标识中的值与该关键变量当前在程序中实际的值是并不相同的,而如果要读取并使用该关键变量的值,应该读取的是其最新的值,基于此考虑,所以在内核内存区域中检索出的值标识中的值与该关键变量当前在程序中的实际的值不相同时,则放弃通过快速读路径从寄存器溢出值中得到的值标识,并再从寄存器进行值标识的检索,以得到最新修改后的值的值标识,进而提高值标识确定的准确性,以准确地得到关键变量当前实际的值,确保程序的准确运行。
在一种可能的设计中,在寄存器的待溢出的值标识中,以内存地址作为关键字进行检索以确定包括该内存地址的目标值标识,再判断该目标值标识包括的值与关键变量当前在程序中的值是否相同,若相同,则该所述目标值标识溢出到内核内存区域中,若不相同,则对程序执行程序篡改处理。
在上述技术方案中,按照前述提供的快读路径从内核内存区域中检索值标识,假如程序中一个函数指针变量被篡改成一个该变量以前曾经采用过的值,这种重放攻击的篡改可能无法检测出来,因为此时通过快读路径读取并转换得到的值可能正好等于该变量以前使用过的值,为了缓解这种可能的重放攻击,在从寄存器中将值标识溢出到内核内存区域时,可以将待溢出的值标识中的值与该关键变量当前在程序中的实际的值进行比较,若两者相同则认为关键变量的值未被篡改,若两则不同则认为关键变量的值可能已被篡改,例如可能已被篡改为之前已经使用过的一个值,即遭遇了重放攻击,此时则可以执行例如终止程序等程序篡改处理,以尽量确保程序的安全运行。
在一种可能的设计中,在关键变量为C++对象虚函数表指针或重要数据变量时,在以关键变量的内存地址作为关键字从安全存储区域中进行检索以确定值标识时,可以先以该内存地址作为关键字在寄存器中进行检索,以确定在寄存器中是否有包括该内存地址的值标识,若有,则将包括该内存地址的值标识确定为该关键变量对应的值标识,若没有,则再以该内存地址作为关键字在内核内存区域中进行检索,以将该内核内存区域中包括该内存地址的值标识确定为该关键变量对应的值标识。
在上述技术方案中,由于C++程序经常在初始化一个对象后立即调用其虚函数,先从寄存器中寻找值标识的方法效率更高,可以取得更好的性能,以及程序可能在对数据变量赋值之后迅速读取其值,首先从寄存器中检索值标识,可以取得更好的性能,基于此考虑,所以对于C++对象虚函数表指针或重要数据变量这些关键变量,其对应的快读路径是优先从寄存器中进行检索值标识,这样可以尽量满足这些关键变量的值的读取特点,从而提高值标识的确定效率,提高程序性能。
在一种可能的设计中,在程序结束运行时,可以清除安全存储区域中存储的数据,即可以清除寄存器和内核内存区域中存储的值标识,也就是释放内存空间,以提高内存的复用率。
第二方面,提供一种数据处理装置,该装置可以计算设备,或者可以是能够支持计算设备中装置,该数据处理装置可以包括第一确定模块、第二确定模块和第三模块,这些模块可以执行上述第一方面任一种设计示例中的方法。例如,第一确定模块,用于在需要读取程序中的关键变量的值时,从安全存储区域中确定与关键变量对应的值标识,其中,关键变量为能够影响程序的执行逻辑的程序变量,值标识是根据关键变量的赋值确定的,安全存储区域包括处理器中的寄存器和内核内存区域,内核内存区域具有对内核可读可写且对应用只读的属性;第二确定模块,用于从安全存储区域中读取值标识,并确定与值标识对应的值;以及第三确定模块,用于将值标识对应的值确定为关键变量的值。
在一中可能的设计中,上述数据处理装置还可以包括存储模块,该存储模块可以执行上述第一方面中的一些可能设计示例中的方法。
第三方面,提供一种数据处理装置,该数据处理装置还可以包括处理器和存储器,存储器用于存储程序指令和数据,存储器与处理器耦合,处理器可以调用并执行存储器中存储的程序指令,用于实现上述第一方面中的任意一种可能设计示例描述的方法。
第四方面,提供一种计算机可读存储介质,所述计算机可读介质上存储有指令,当所述指令在计算机上运行时,使得计算机执行上述第一方面中的任意一种可能设计示例描述的方法。
第五方面,提供一种计算机程序产品,所述计算机程序产品包含有指令,当所述指令在计算机上运行时,使得计算机执行上述第一方面中的任意一种可能设计示例描述的方法。
第六方面,提供了一种芯片系统,该芯片系统包括处理器,还可以包括存储器,用于实现上述第一方面中的任意一种可能设计示例描述的方法。该芯片系统可以由芯片构成,也可以包含芯片和其他分立器件。
上述第二方面至第六方面及其实现方式的有益效果可以参考对第一方面的方法及其实现方式的有益效果的描述。
附图说明
为了更清楚地说明本发明实施例的技术方案,下面将对本申请实施例中所需要使用的附图作简单地介绍,显而易见地,下面所介绍的附图仅仅是本申请的一些实施例,对于本领域普通技术人员来讲,在不付出创造性劳动的前提下,还可以根据这些附图获得其他的附图。
图1为现有的计算设备的架构示意图;
图2为本申请实施例中的数据处理方法的流程图;
图3为本申请实施例中的检视二进制可执行程序体的示意图;
图4为本申请实施例中的基于寄存器保护函数调用返回地址的流程示意图;
图5为本申请实施例中的函数调用的示意图;
图6为本申请实施例中的关键变量在程序中和安全存储区域中的示意图;
图7为本申请实施例中的数据处理装置的结构框图;
图8为本申请实施例中的数据处理装置的结构示意图。
具体实施方式
为使本发明实施例的目的、技术方案和优点更加清楚,下面将结合本发明实施例中的附图,对本发明实施例中的技术方案进行清楚、完整地描述。
以下,对本申请实施例中的部分用语进行解释说明,以便于本领域技术人员理解。
1、控制流图(control flow graph,CFG),也叫控制流程图,是一个过程或程序的抽象表现,是用在编译器中的一个抽象数据结构,由编译器在内部维护,是用图的形式表示一个程序执行过程中会遍历到的所有路径。
2、控制流劫持攻击,是指通过劫持程序的控制流去执行非预期的恶意代码逻辑的攻击方式,这些恶意代码逻辑通常是通过拼接程序中现有的代码片段(gadgets)或者调用Libc(C库)中的敏感函数(如mprotect函数、execve函数、setuid函数)实现的。通过控制流劫持攻击,攻击者可以获得shell(解释和执行系统命令的程序),破坏和窃取程序中的数据,进而实现提权等攻击目的。产生控制流劫持的原因是在于程序中的一些指针被攻击者利用程序中的内存安全错误(memory safety errors)进行了篡改,从而劫持了程序的控制流并转移到非预期的代码位置。非预期的代码位置可以是Libc中的敏感函数,程序原本不会调用该函数或者不会以同样的参数值调用该函数。非预期的代码位置也可以是程序中某些函数代码的中间位置,这样可以跳过参数安全检查或者实现代码片段拼接,而程序正常情况下调用某个函数时是从函数的起始位置开始执行的。
3、内存安全错误,包括程序中存在的空间内存错误(spatial memory errors)和时间内存错误(temporal memory error),空间内存错误主要包括缓冲区溢出、时间内存错误在释放后又使用(use after free)、重复释放(double free)等。很多内存安全错误都可以被攻击者利用以实现执行未授权的操作,这样就形成了内存安全漏洞,而内存安全漏洞一直是困扰C/C++程序安全性的顽疾,现在还没有可靠的方法能够检测或者排除大型程序中的所有内存安全漏洞。当一个程序上线或者被部署时,无论经过多少测试,一般都难以完全确定出该程序中残存了多少内存安全漏洞以及这些内存安全漏洞可能引发的攻击的严重程度。只要程序中有内存安全漏洞,就给攻击者留下了利用这些漏洞实现程序控制流劫持攻击的可能。例如,在2018年前4个月,由通用漏洞披露(common vulnerabilities andexposures,CVE)公布的超过280个内存安全漏洞中,其中接近一半有可能会导致控制流劫持,再例如2018年3月,一个缓冲区溢出漏洞在一款常用的Email服务器软件中被发现,全球有超过40万台Email服务器受到影响。
4、数据攻击,是指通过在程序运行时通过篡改程序中的数据的方式实现的程序攻击,数据攻击与控制流劫持攻击所篡改的目标对象不同,控制流劫持攻击篡改的是程序中的指针,而数据攻击篡改的是程序中的数据,数据攻击又可以称作data attacks或者non-nontrol data attacks。在程序中被篡改的数据通常是一些能够影响程序处理逻辑的重要数据,例如程序中的函数参数、全局变量、表示用户是否已经认证通过的认证标志变量、表示可执行程序所在目录的目录变量,等等。
通过对数据攻击进行进一步的研究,发展了面向数据编程(data orientedprogramming,DOP)和控制流弯曲(control flow bending,CFB)等攻击,但这些都可以看作是数据攻击的变种或者演化形式。在数据攻击中,攻击者不试图直接劫持控制流,而是通过篡改对程序逻辑有影响的数据变量以实现对程序处理逻辑的扭曲,但从攻击方法上,攻击仍然需要利用程序中的内存安全漏洞实现对程序数据的篡改,这与控制流劫持攻击类似,不同之处,在于这里篡改的目标是程序中的数据变量而不是指针。
5、本申请实施例中的“多个”是指两个或两个以上,鉴于此,本申请实施例中也可以将“多个”理解为“至少两个”。“至少一个”,可理解为一个或多个,例如理解为一个、两个或更多个。例如,包括至少一个,是指包括一个、两个或更多个,而且不限制包括的是哪几个,例如,包括A、B和C中的至少一个,那么包括的可以是A,B,C,A和B,A和C,B和C,或A和B和C。“至少两个”,可理解为两个或更多个。同理,对于“至少一种”等描述的理解,也是类似的。“和/或”,描述关联对象的关联关系,表示可以存在三种关系,例如,A和/或B,可以表示:单独存在A,同时存在A和B,或单独存在B这三种情况。另外,字符“/”,如无特殊说明,一般表示前后关联对象是一种“或”的关系。
以及,除非有相反的说明,本申请实施例提及的“第一”、“第二”、“第三”等序数词是用于对多个对象进行区分,不用于限定多个对象的顺序、时序、优先级或者重要程度。
为了更好地理解本申请实施例提供的技术方案,下面先介绍本申请实施例的技术背景。
如图1所示,一般的计算设备(例如计算机)包括两个物理上分离的关键部件,即处理器(processor)和内存,处理器例如是中央处理器(central processing unit,CPU),处理器通过总线与内存连接。内存存储了要执行的程序的指令和数据,处理器从内存中读取要执行的指令和待处理的数据,执行指令完成数据计算,将计算结果写回到内存中。每个内存的存储单元都具有一个唯一的地址,程序和处理器都通过地址访问内存存储单元。处理器中包括有寄存器,处理器可以使用其中的寄存器来辅助指令执行和执行数据计算。寄存器是位于处理器内的具有较小存贮容量的高速存贮部件,处理器对寄存器的访问可以直接在处理器内部完成,无需经过总线,访问速度比处理器访问内存的速度快很多,例如可以快上百倍。寄存器还有一个特点是,每个寄存器都有自己的名字,对寄存器的访问只能通过使用寄存器的名字进行,换言之,是通过寄存器的名字来实现对寄存器的访问的。
现在处理器中有些寄存器是几乎所有程序都会使用的,而另外一些专用寄存器只有某些特殊类型的程序才会使用。如X86 64位处理器有RAX、RBX、RCX、RDX、RSI、RDI、RSP、RBP、R8-R15等16个通用寄存器,还提供8个64位MMX寄存器,8个128位XMM寄存器,16个256位AVX寄存器,4个128位MPX寄存器等专用寄存器。其中的16个通用寄存器用于数据计算、栈结构维护等,几乎所有程序都会使用;而XMM和MMX寄存器一般只有进行向量计算的应用程序才会使用MPX寄存器只有使用MPX进行缓冲区界限检查的程序才会使用。又例如ARM 64位的处理器有31个64位的通用寄存器x0-x30,32个128位SIMD寄存器v0-v31,其中的31个通用寄存器大部分程序常用,而SIMD寄存器主要由处理图像和视频的程序使用。再例如32位的X86处理器中的寄存器包括EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP这8个通用寄存器,8个64位MMX寄存器,8个128位XMM寄存器,其中的8个通用寄存器几乎所有程序都会使用,而MMX和XMM寄存器一般只有进行向量计算的程序才会使用。
程序包括内核程序和应用程序,内核程序运行在内核态上,具有较高的访问权限,可以直接访问很多硬件资源,而应用程序一般运行在用户态,具有相对较低的访问权限,一般需要借助内核程序提供的服务接口去访问硬件资源。应用程序包括应用程序自身代码和程序所依赖的共享库,它们一般都是各自独立编译,然后进行整体链接。应用程序的自身代码是一个链接单元,每个共享库也各自都是一个链接单元。
一个C/C++程序中一般至少有三种代码指针(code pointers)可能被攻击者篡改以实现控制流劫持,这三种指针包括:间接函数调用使用的函数指针变量,函数调用时的函数调用返回地址和间接跳转使用的代码位置指针。这三种指针可以影响和决定程序的分支指令(branch instructions)的目标地址(target address),且可以被篡改。额外的,C++程序中每个使用虚函数的对象都有一个指向虚函数表(virtual method table,VTable)的指针,例如以vptr表示该指向虚函数表的指针,如果这个vptr指针的值被篡改,也会引起控制流劫持攻击。前面所列举的几种指针的篡改都可能导致程序的控制流劫持,为了便于描述,本申请实施例中将前面所列举的这些以及其它可能引起控制流程序的指针统称为代码指针,即在本申请实施例中,代码指针至少包括函数调用返回地址、函数指针变量,C++对象中指向虚函数表的指针和间接跳转使用的代码位置指针,其中,C++对象中指向虚函数表的指针又可以称作C++对象虚函数表指针。
需要说明的是,C++语言是对C语言的一种扩展,所以C++程序中也存在函数指针变量,函数调用返回地址和代码位置指针,且也都可以被篡改,这与C语言程序是一样的,也就是说,前述代码指针中的函数调用返回地址、函数指针变量和间接跳转使用的代码位置指针是同时针对C程序和C++程序的。除此之外,由于C++对象虚函数表指针是C++程序中的独有的一种指针,相对于C程序来说,所以C++程序还单独面临C++对象虚函数表指针被篡改而导致控制流劫持的问题。
每个代码指针都有两个重要属性:地址与值,其中的地址是指内存地址。具体来说,函数指针变量位于程序的内存空间中,本身具有一个内存地址,而它的值指向一个函数;函数调用返回地址保存在函数调用发生时当前栈的栈顶,它的值是程序中的某个函数调用位置;C++对象的vptr指针位于内存中对象的起始位置,其值指向对象使用的虚函数表;间接跳转使用的代码位置指针一般位于内存堆或栈上,且其值为程序中的某条指令的地址。在程序运行过程中,代码指针会被程序赋值,其值也会被程序读取和使用,所谓赋值就是代码指针本身所占据的内存单元被初始化或者原先存有的值被修改的过程,读取和使用都包含了对代码指针本身所占据的内存单元中保存的值的读取过程,例如程序运行时,函数指针变量会被赋值指向某个函数,例如,*fptr=fun1,其值随时可能被读取,例如,*fptr2=*fptr,在函数调用时其值会被使用,例如,call*fptr。函数调用返回地址在函数调用时其值会被保存到当前的栈顶位置,当函数执行完毕返回时其值会从栈顶被读取出来使用。C++对象虚函数表指针,当对象被初始时被赋值,当该对象调用其虚函数时被使用。间接跳转使用的代码位置指针在初始化时被赋值,程序执行过程中可以随时重新赋值或者读取其值,当执行间接跳转时其值被使用,注意这里的“使用”,不会修改代码指针的值,而只是获取其值,所以本申请中将这种“使用”认为是一种读操作。
如前所述,程序存在控制流劫持攻击和数据攻击等程序攻击问题,程序的安全性较差。针对控制流攻击和数据攻击,目前提出了一些相应的解决方案,以下列举说明。
现有的一种防御控制流劫持技术是控制流完整性技术(control flowintegrity,CFI),CFI技术的核心思路是限制程序运行中的分支转移,使之始终处于原有的控制流图所限定的范围内。技术方案一般是通过编译器分析程序的控制流图,获取间接分支指令(包括函数返回、间接函数调用、间接跳转)目标地址的白名单,并在运行过程中,核对间接分支指令的目标是否在白名单中。由于控制流劫持攻击往往会违背程序原有的控制流图,CFI使得这种攻击行为难以实现,从而保障软件系统的安全。
CFI从实现角度上,被分为细粒度和粗粒度两种。细粒度CFI严格控制每一个间接分支指令的转移目标,而粗粒度CFI则是将一组类似或相近类型的目标归到一起进行检查,以降低开销。但是,细粒度CFI由于检查代价高昂通常会造成严重的性能下降,所以在实际中难以得到应用,粗粒度CFI通过牺牲安全性以换取性能,通常提供的安全保障比较差,通过实践已经证明了在粗粒度CFI下,仍然会存在调用位置代码片段(call site gadgets)和面向返回的代码片段(return oriented gadgets)。粗粒度CFI通常只能确保函数调用时程序控制流转移到函数的开始,而不能确保转移到正确的目标函数(程序合法控制流所要求的目标函数),这造成call site gadgets存在。粗粒度CFI通常只能确保函数调用返回时,返回到某个函数调用位置,而不能保证返回到该函数最近的调用位置,这造成了returnoriented gadgets存在。需要指出的是,无论细粒度CFI还是粗粒度CFI都不可能完全准确地确保间接分支的目标地址的准确性,因为在很多情况下一个间接分支指令的目标地址只能在程序运行时才能确定,在程序编译阶段构建控制流图时不可能确定其准确的目标地址,这是CFI技术的一个固有缺限。并且,由于数据攻击并没有使得程序执行产生新的额外执行流,所以CFI技术不能用于检测和防御程序中的数据攻击。
现有的另一种防御控制流劫持技术是代码指针完整性技术(code pointerintegrity,CPI),CPI技术的思路是把代码指针放到一个专有的内存区域中进行保护,使得攻击者无法篡改专有内存区域中的代码指针。但是,CPI技术的一个弱点是难以对抗信息泄露攻击,CPI技术的安全性依赖于存放代码指针的专有内存区域的位置的机密性,攻击者如果知道了或者通过某些方式能够探测该内存区域的地址,则可以通过程序内存安全漏洞篡改其中的代码指针,进而造成控制流劫持攻击。泄露该内存区域的方法主要有两种:指针泄露和暴力探测。指针泄露通过Linux中的longjmp/setjmp函数或者C++的异常处理可以很容易泄露安全栈(safestack)的位置,暴力探测可以通过内存侧信道或memory allocationoracles去确定存放代码指针的内存区域。程序信息泄露,特别是内存位置泄露,是程序安全中的一个很大威胁,近些年来由于攻击者分析探测技术的进步,依赖于内存位置机密性的安全机制受到了很大的挑战。
现有的一防御数据攻击的主要方法是数据流完整性技术(data flow integrity,DFI)。DFI技术首先通过静态分析标记所有变量的赋值和引用,生成每个变量引用的合法集合,构建数据流图。其次动态监控程序的每次赋值操作,更新对应的标志且检查是否合法,若发现不合法的赋值操作,则触发警报处理过程。但是,DFI技术需要监控数据的赋值并进行复杂的安全检查,性能开销巨大。
通过对现有的防御攻击技术进行分析,可见现有的防御攻击技术要么只能防御控制流劫持攻击,要么只能防御数据攻击,并且防御的有效性也较低,或者需要较大的性能开销。也就是说,现有中的程序在运行时存在诸多安全威胁,并且,现有技术中对程序的安全攻击不仅涉及到代码指针的篡改攻击,还包括对程序中的一些重要数据变量的安全攻击,然而,现有技术中还没有针对这些攻击统一而有效的保护方式。
鉴于此,本申请实施例提供一种在程序运行中的数据处理方法,通过该方法可以对程序中的关键变量进行有效保护,具体来说,在关键变量被赋值之前或者之后,将关键变量的赋值转换成对应的值标识,进而再将值标识存储在安全存储区域中,该安全存储区域例如是处理器中的寄存器或者是对内核可读可写但对应用只读的内核内存区域中,进一步地,在需要读取该关键变量的值时,则可以从该安全存储区域中读取该关键变量的值标识,然后再将该值标识对应的值作为该关键变量实际的值进行调用,因为值标识一般情况下是存放在寄存器中的,溢出的值标识被存放在对内核可读可写但对应用只可读的内核内存区域中,攻击者难以篡改值标识。而且利用值标识可以很容易的检索到变量的真实的值,本方法可以减小关键变量被篡改的可能,提高程序的安全。
在本申请实施例中,关键变量是能够影响程序的执行逻辑的程序变量,可以这样理解,关键变量被篡改的话则会影响程序的执行逻辑发生变化,本申请实施例中的关键变量包括前述的代码指针,同时也可以包括前述的重要数据变量,所以,通过本申请实施例的技术方案不仅可以防止攻击者通过篡改代码指针进行的控制流劫持攻击,还可以防御攻击者篡改程序的重要数据变量进行的数据攻击,即通过本申请实施例的技术方案可以实现针对控制流劫持攻击和数据攻击同时进行防御的统一、有效的程序保护方式。
基于前述设计思想,本申请实施例提供一种程序运行中的数据处理方法,该方法可以在程序运行中执行,用以提高程序的安全性。请参见图2所示,本申请实施例中的程序运行中的数据处理方法的流程描述如下。
S21:在需要读取程序中的关键变量的值时,从安全存储区域中确定与该关键变量对应的值标识;
S22:从安全存储区域中读取关键变量对应的值标识,并确定与该值标识对应的值。
S23:将值标识对应的值确定为关键变量的值。
S24:使用关键变量的值。例如执行函数调用或者函数返回或者跳转或者解析虚函数表。
本申请实施例中的关键变量是指能够影响程序的执行逻辑的程序变量,可以这样理解,关键变量被篡改之后,如果调用被篡改后的关键变量运行程序的话则可能引起程序的执行逻辑的变化,进而使得程序陷入安全问题。例如,本申请实施例中的关键变量可以指前述介绍的代码指针和重要数据变量,这些数据的篡改都会造成程序执行逻辑的变化,代码指针的篡改可能会引起控制流劫持攻击,而重要数据变量的篡改可能造成数据攻击。也就是说,该代码指针是能够引起程序执行逻辑发生变化的指针,该代码指针例如可以包括函数调用返回地址、函数指针变量、C++对象虚函数表指针、代码位置指针以及其它能够引起程序执行逻辑发生变化的指针。对应的,该重要数据变量也是指能够引起程序执行逻辑发生变化的程序变量,该重要数据变量例如包括影响程序的执行逻辑的函数参数或全局变量,等等。
本申请实施例中的值标识,可以理解为是能够表示关键变量的值的标识信息,但值标识与其对应的值不同,例如,可以对关键变量按照一定的规则进行编码处理,进而将编码处理后得到编码信息作为该关键变量的值对应的值标识,一个关键变量的值与其对应的值标识,长度可以相等,或者也可以不等,例如,关键变量的值为16位,而经过编码之后得到的值标识的长度是4位,又例如,关键变量的值为8位,而经过编码之后得到的值标识的长度是12位,等等,换言之,关键变量的值的长度可以大于对应的值标识的长度,或者也可以小于对应的值标识的长度。需要说明的是,本申请实施例中所说的关键变量的值的长度以及值标识的长度,可以是指二进制长度,即所包括的二进制的位数。
在具体实施过程中,可以根据关键变量的变量类型选择对应的转换方式将其值转换为对应的值标识,也就是说,可以预先设定关键变量与值标识转换规则之间的对应关系,在对一个关键变量的值进行值标识转换时,可以首先确定该关键变量的变量类型,进而再根据对应的值标识转换规则对其值进行转换处理,进而得到对应的值标识。另外,在进行值标识转换时,可以仅将关键变量的值作为转换基础,即仅根据关键变量的值来进行值标识的转换,或者在另一种实施方式中,在关键变量的值的基础上,还可以结合其它数据一起作为转换基础进行值标识的转换,例如对于某些关键变量来说,可以将该关键变量的值和内存地址一起作为转换基础进行值标识的转换。通过值标识的转换方式,可以对关键变量的值进行一定程度的保护,这样可以避免攻击者直接的获取到关键变量的值从而进行篡改而导致安全问题,另外,通过安全存储区域存储值标识的方式可以进一步地降低值标识被攻击者直接获取的可能,进一步地提升程序的安全性。
本申请是实施例中的值标识是保存在安全存储区域中,而该安全存储区域包括处理器中的寄存器和内核内存区域,该内核内存区域具有对内核可读可写但对应用只读的属性。具体来说,是将值标识直接存储在寄存器中,当寄存器存满时,则可以将先前存储的值标识溢出到内核内存区域,由于内核内存区域对内核可读可写但对应用只读,所以这样可以防止攻击者对值标识进行篡改,进而确保值标识的安全。
本申请实施例中的内核内存区域可以是内存中的一块区域,但是其具有对内核可读可写但对应用只读的特殊属性。该内核内存区域可以是通过内核模块创建和维护的,该内核模块可以被动态加载到内核空间中,内核模块申请一段物理内存,可以通过页表建立该物理内存到内核态虚拟地址和用户态虚拟地址的两个映射,内核模块通过设置页表映射上的权限确保内核内存区域对应用程序只读,这就使得攻击者无法通过应用程序的内存安全漏洞篡改寄存器的溢出值,同时,内核模块通过设置页表映射上的权限确保安全内核内存区域对内核可读可写,这就使得程序在需要往寄存器中保存数据而寄存器没有可用空间的情况下,可以通过陷入内核把寄存器中存储的值标识溢出到该段内核内存区域中,从而使得寄存器再次可以用于保存新的值标识,在这种情形下,攻击者除非能够直接控制内核,否则不能篡改内核内存区域中的内容,从而提高值标识的安全。
本申实施例中,选择处理器中的寄存器保护程序中的关键变量的值标识,是基于以下考虑。第一,攻击者利用程序内存安全漏洞无法篡改寄存器的值,因为寄存器都位于处理器之内,与内存分离,并且每个寄存器都只能通过寄存器的名字才能访问。现在计算机通常都具备数据执行保护(data execution prevention,DEP,)能力,攻击者无法向程序中注入攻击代码,所以攻击者无法篡改寄存器的值。第二,寄存器位于处理器之内,处理器访问寄存器的速度比访问内存的速度高很多,这可以在一定程度上优化本方案的性能。第三,可以抵抗信息泄露攻击,传统CPI技术中使用一个隐藏的专有内存区域存放代码指针,而该区域的位置很容易泄露,申请实施例中使用寄存器保护程序中的关键变量的值标识,而寄存器都是公开且只能通过寄存器的名字访问,这样攻击者无法泄露或者篡改寄存器的值。
处理器的寄存器包括几乎所有程序都会使用的通用寄存器,也包括只有极少数应用才会使用的专用寄存器,在一种可能的寄存器分配方案中,可以把通用寄存器预留给程序原有代码逻辑使用,而使用类似MMX、MPX等专用寄存器来保护程序中的关键变量的值标识,这样可以避免干扰程序原有代码逻辑并尽量减少寄存器溢出。但可以理解的是,本申请实施例并不限定只使用专用寄存器保护关键变量的值标识,换言之,任何寄存器都可以用来保护关键变量的值标识,本申请实施例不做限制。关于寄存器的选用,可以很多组合方案,作为一种可选的方案,本申请实施例可以使用%mm0-5这6个64位的MMX寄存器保护直接函数调用返回地址这种关键变量的值标识,使用%mm6和%xmm15保护间接和外部函数调用这些关键变量的值标识,使用4个128位MPX寄存器保护函数指针变量、代码位置指针、C++对象虚函数表指针和程序重要数据变量这些关键变量的值标识。
本申请实施例中,在程序对程序中的关键变量进行赋值之前或之后,对关键变量的值进行转换处理以得到对应的值标识,并保存到寄存器中进行安全保护,当程序需要读取该关键变量的值时,可以从寄存器或寄存器的溢出值(即内核内存区域)中确定对应的值标识,并根据确定的值标识反向得到关键变量的值,进而提供给程序使用,以实现程序对该关键变量的调用。
如前所述的,在把关键变量的值标识保存到寄存器中而寄存器有没有足够空闲空间时,可以将寄存器中已经存储的值标识溢出到一段对内核可读可写但对应用只读的内核内存区域,使得寄存器空闲可用,从而可以通过空闲的寄存器再存储新的值标识。另外,当需要读取某个值标识时,若此时寄存器为空的话则可以先从内核内存区域中恢复寄存器的溢出值,进而再从恢复溢出值的寄存器中查找匹配的值标识,或者在另外一些读取方式中,也可以直接从内核内存区域中直接查找匹配需要的值标识。
在本申请实施例中,在获得关键变量的值对应的值标识之后,可以将该关键变量的值、对应的值标识、该关键变量的值与对应的值标识之间的对应关系存储在转换表中,以通过该转换表记录该关键变量的值和与其对应的值标识之间的对应的关系。相应的,在根据值标识反向确定对应的关键变量的值时,则可以通过查找该转换表的方式来确定与值标识对应的值。
在一种可能的情形下,若值标识的长度小于其对应的关键变量的值的长度,这样可以降低寄存器的空间占用,在大多数情况下值标识的长度比关键变量的值的长度短,例如关键变量的值是64位,而值标识可能只有4位或者16位,这样可以降低对寄存器的空间占用,以尽量减少寄存器溢出。例如,现在64位寄存器的寄存器大多数是64位或者128位,如果直接把关键变量的值本身放到寄存器中,一个寄存器只能存放1-2个关键变量的值就需要溢出,而如果把值标识编码成长度位4位的值标识,一个寄存器在需要溢出之前可以存储几十个值标识,从而提升寄存器的存储利用率,尽量减少寄存器溢出。
如前所述的,本申请实施例中把值标识往寄存器中保存时,如果寄存器空间没有空闲,则可以把寄存器中存储的数据溢出到一段内核可读可写但对应用只读的内核内存区域以使得寄存器空闲可用,而溢出是一个性能代价相对高昂的操作,通过把关键变量的值转换成值标识的方式可以起到压缩数据的效果,从而尽量降低发生寄存器溢出的频。需要说明的是,并不是在任何情况下,关键变量的值标识都比关键数据的值本身的长度短,在某些情形下,留对于如函数指针变量这种关键变量来说,根据其对应的值标识方式,关键变量的值标识可能比其本身的长度要长,而本申请实施例中所说的节省寄存器空间占用,可以是对于平均意义而言的。
由于寄存器数量和每个寄存器的空间是有限的,这意味着会出现需要向寄存器中保存值标识而寄存中没有足够空间可用的情况。这种情况下,本方法让应用程序陷入到内核中,把寄存器值溢出到一段对应用程序只读但对内核程序可读可写的内核内存区域。该内核内存区域中保存的寄存器值标识称为寄存器溢出值。该段内存区域是通过一个内核模块创建和维护的,该模块可以被动态加载到内核空间中。内核模块申请一段物理内存,通过页表建立该物理内存到内核态虚拟地址和用户态虚拟地址的两个映射。内核模块通过设置页表映射上的权限确保内核内存区域对应用程序只读,这就使得攻击者无法通过应用程序的内存安全漏洞篡改寄存器溢出值。内核模块通过设置页表映射上的权限确保内核内存区域对内核可读写,这就使得程序在需要往寄存器中保存数据而寄存器没有可用空间的情况下,可以通过陷入内核把寄存的值溢出到该段内核内存区域中,从而使得寄存器再次可以用于保存新的值标识。攻击者除非能够直接控制内核,否则不能篡改内核内存区域中的内容。
本申请实施例中,可以通过编译器扩展插件或者修改编译器代码的方式实现,以提升编译器编译产生的程序的安全性。使用本方法的编译器,在编译程序的程序源代码以及其所依赖的共享库的时,可以在中间表示代码(intermediate representations,IR)层上插入使用寄存器对程序中的关键变量进行安全保护的代码逻辑,使得程序可以抵抗控制流劫持攻击和数据攻击。程序源代码的编写者,可以专注于程序的业务逻辑,而无需花费精力关心程序的安全性,因为安全性可以由编译器按照我们的方法自动提供。
编译器对程序源代码进行编译,产生二进制的可执行程序体。可执行程序体包含二进制可执行代码,在需要执行时,操作系统为其创建进程,分配硬件资源,装载程序体。程序体开始运行后,会频繁调用其所依赖的共享库(特别是LibC)。共享库经常是对操作系统(operating system,OS)提供的系统调用的一种封装,以便使得程序可以更容易的访问OS提供的服务,并增强程序的独立性。共享库一开始也是源代码组成的,需要经过编译器编译才能得到可以被应用调用的二进制形式的共享库,本申请实施例用于编译器在编译程序源代码产生二进制可执行程序体的过程中,可以确保产生的二进制程序更加安全,本申请实施例可以通过检视所产生的二进制可执行程序体确定,如图3所示。
在本申请实施例中,在程序中的关键变量被赋值之前或者之后,可以将关键变量的赋值转换成对应的值标识,进而再将值标识存储在安全存储区域中,该安全存储区域例如是处理器中的寄存器或者是对内核可读可写但对应用只读的内核内存区域中,进一步地,在需要读取该关键变量的值时,则可以从该安全存储区域中读取该关键变量的值标识,然后再将该值标识对应的值作为该关键变量实际的值进行使用,因为值标识一般情况下是存放在寄存器中的,溢出的值标识被存放在对内核可读可写但对应用只可读的内核内存区域中,攻击者难以篡改值标识,而且利用值标识可以很容易的检索到变量的真实的值,本方法可以减小关键变量被篡改的可能,提高程序的安全。
也就是说,本申请实施例提供了一种高性能的能够抵御信息泄露攻击的程序中的关键变量的保护方法,既可以保护程序中的代码指针不会因为程序中的内存安全漏洞而被篡改以防御控制流劫持攻击,又可以保护程序中的重要数据变量不会因为程序的内存安全漏洞而被篡改以抵御数据攻击。本方案可以提供较强的程序安全保障,首先不依赖于某些内存区域位置的机密性,能够抵抗信息泄露攻击,其次为程序的关键变量提供高精度的保护,避免粗粒度CFI的问题,例如可以确保间接函数调用时控制流总是可以转移到正确的目标地址和函数返回时总是转移到正确的目标地址,本申请实施例的方案在保障程序安全的同时不明显降低程序的执行性能,可以实现较好的技术实用价值。
基于上述技术思路,本申请实施例分别设计了针对不同类型的关键变量的具体保护方式,为了便于理解,以下分别针对多种关键变量的程序保护方案进行说明。
一、针对函数调用返回地址的安全保护。
函数调用,可以包括函数外部调用和函数内部调用,而函数内部调用又可以包括直接函数调用和间接函数调用,以下分别进行说明。
本申请实施例用于保护函数调用返回地址,包括直接函数调用返回和间接函数调用返回两种情况下的返回地址。本申请实施例为每个函数分配一个寄存器,用于保存该函数的返回地址的值标识。不同的函数可能使用同一个寄存器,但每个函数使用的寄存器都是固定的。每个函数调用返回地址的值标识代表了一个真实的返回地址,从函数调用返回地址的值标识到函数调用返回地址的值映射可以保存在入前介绍过的转换表中。
在关键变量为函数调用返回地址时,通过与该关键变量的变量类型对应的转换规则对该关键变量的赋值进行转换处理,以得到与该赋值对应的值标识,可以包括:将被调用函数的函数调用位置确定为该被调用函数的函数调用返回地址,进而再根据该被调用函数的调用方式对应的值标识分配方式,为每个函数调用位置分配对应的一个值标识,以得到每个函数调用位置对应的值标识,其中,该关键变量当前的函数调用位置对应的值标识则为其赋值对应的值标识。由于一个程序中所有可能的函数调用返回地址集合是有限的,它等同于程序中所有的函数调用位置,所以对程序中函数调用位置分配值标识就相当于实现了对函数调用返回地址分配值标识,所以可以编译过程中通过扫描程序中所有的函数调用的位置一确定所有可能的函数调用返回地址,并为每个函数调用位置分配一个不同的值标识,进而,则可以得到每个函数调用返回地址对应的值标识。通过巧妙的利用函数调用位置与函数调用返回地址之间的对应关系,并且基于函数调用位置预先可知的特性,进而可以为每个函数调用位置分配一个不同的值标识,进而可以针对函数调用返回地址这种关键变量,提供了一种快速得到一个程序中每个被调用函数的每个函数调用返回地址的值标识,可以在一定程度上提高值标识的分配效率。另外,还可以根据函数调用方式的不同采用对应不同的值标识分配方式,可以结合各种函数调用方式的调用特点进行对应的值标识的分配,针对性更强,同时针对不同函数调用方式的函数的值标识的分配方式更加灵活。
对于寄存器分配,一种可能的分配方式是使用%mm0-5保存直接函数调用返回地址的值标识,使用%mm6保存间接函数调用和外部函数调用的返回地址的值标识,并可以使用%xmm15寄存器来临时保存%mm6中溢出的内容。
请参见图4,图4给出了一个基于寄存器保护函数调用返回地址的方法示例。函数foo被分配使用reg1寄存器,函数fun2被分配使用reg2寄存器。每个函数调用位置都有一个划线并以冒号结束的标号,这些函数调用位置就是将来函数调用的返回位置。标号在源代码中是不存在的,但标号所代表的函数返回位置是真实存在的。例如函数foo调用函数fun1,fun1执行完毕时应该返回到fun1_ret_1的位置。函数fun1调用fun2,fun2执行完毕时应该返回到fun2_ret_1的位置。函数foo在步骤1调用函数func1,然后fun1在步骤2调用函数fun2,然后函数fun2在步骤3再次调用fun1,然后函数fun1在步骤4执行完毕返回时,需要返回到函数fun2中的fun1_ret_2位置。按照本方法,此时从reg1中弹出返回地址值标识2,值标识2经过fun1转换表得到真实返回地址fun1_ret_2,然后程序返回到fun1_ret_2。
当一个寄存器没有足够的空间来保存一个函数调用返回地址的值标识时,可以通过插入的代码使得应用程序陷入到内核中,并且把寄存器内容溢出到内核内存区域,以使得该寄存器再次空闲可用。当函数返回时,可以从寄存器中取出函数调用返回地址的值标识。如果寄存器为空,则寄存器的内容可以从内核内存区域中的寄存器溢出值进行恢复。从内核内存区域恢复寄存器的溢出值,可以恢复所有用于保存函数调用返回地址的值标识的寄存器,也可以只恢复当前为空的寄存器,例如可以恢复所有用于保存函数调用返回地址的值标识的寄存器,这可以降低溢出恢复的频率,提高性能。在恢复所有用于保存函数调用返回地址的值标识的寄存器时,有些寄存器此时可能并不为空,这时可以从内核内存区域中恢复足够的内容使得这些寄存器能够填满即可。
由于内核内存区域对应用程序只读而对内核程序可读可写,所以当应用程序在溢出寄存器时需要陷入到内核中才能执行,由于内核内存区域对应用程序可读,程序在恢复寄存器时可以直接读取内核内存区域中的寄存器溢出值。由于陷入到内核是一个性能耗费较高的操作,本申请实施例中可以尽量减少陷入到内核的次数,进而提升性能。
对于函数调用返回地址的值标识的分配方法,可以按照如下处理。
一个程序中所有可能的函数调用返回地址集合是有限的,它总是等同于程序中所有函数调用位置,所以对程序中函数调用位置分配值标识就相当于实现了对函数调用返回地址分配值标识。本申请实施例在编译过程中通过扫描程序中所有的函数调用的位置确定所有可能的函数调用返回地址,并为每个函数调用位置分配一个数据类型为正整数的值标识。
本申请实施例可以通过扫描链接过的程序以构建转换表,可以使用转换表记录从值标识到函数返回地址的映射,转换表有多个且都是只读的。
针对直接函数调用,所有调用同一个函数的直接函数调用位置被放到同一个集合中,每个调用位置都可以被分配一个类型为正整数的值标识。每个集合中的值标识从数字1开始进行顺序编码,每个值标识该集合内是唯一的,一个集合中没有两个调用位置具有相同的值标识,这确保了从不同的函数调用位置调用同一个函数时可以返回到各自不同的位置。每个被调用函数都有自己的转换表,不同的函数可以有不同的转换表,转换表记录了从值标识到对应的函数调用位置的映射,这是对于直接函数调用的处理方法,示例可以参考图4中的fun1转换表。
针对间接函数调用,对于类似”call*%rax”的间接函数调用,本申请实施例可以把一个链接单元中的所有间接函数调用位置放到同一个集合中,并为每个调用位置分配一个唯一的值标识,这样在一个链接单元内的所有间接函数调用位置都有不同的值标识。在程序中,对于一个函数,如果它的符号或者地址在某条代码语句中被引用,则该函数就是地址采用的(address taken),一个函数只有是地址采用的才可能被间接调用到,本申请实施例实际上使得一个链接单元内的所有地址采用的函数都使用同一个转换表。
程序自身的代码和它所依赖的共享库(shared libraries)通常是各自独立编译和施加安全保护的,也就是他们处在不同的链接单元中。程序自身的代码和它所依赖的共享库对于各自的地址采用的函数使用各自不同的转换表,造成了一个程序中不同链接单元的间接调用位置的值标识可能冲突,因为程序在运行时是与其依赖的共享库运行在同一个进程空间中。为了解决此问题,本方法通过在加载后对共享库中的所有间接函数调用位置重新调整值标识的方法予以解决。一个程序的所有链接单元的用于间接函数调用的转换表在程序运行之前被合并,每个链接单元都可以获得它原来的转换表在合并后的转换表中的起始位置作为本链接单元的转换偏移量。共享库的一个间接函数调用位置在间接调用一个函数时,会把该间接函数调用位置在原来转换表中映射的值标识与本链接单元的转换偏移量相加,得到一个对整个程序唯一的函数调用返回地址值标识并压入到寄存器中。每个共享库的转换偏移量在合并后被设置成只读的。
由于外部函数调用,也就是跨越链接单元的函数调用(如程序自身代码调用共享库中的函数的情况),可以采用与间接函数调用相同的处理方法,这是因为这种情况与间接函数调用的情况类似,对于一个确定的被调用函数而言,其确切的函数调用位置数量是不可知的,因而无法在编译时就准确地确定其值标识的确切长度。
本申请实施例在程序编译时确定所有函数返回地址的长度。对于一个函数,其在一个链接单元中的所有直接函数调用位置可以在编译时准确确定出来,所以其直接函数调用位置值标识的长度正好可以描述所有的直接函数调用位置,例如一个函数bar有6个直接函数调用位置,则值标识的长度为3位,因为3位二进制最多可以表示8个直接函数调用位置,而2位二进制最多表示4个直接函数调用位置,不足以描述所有的6个直接函数调用位置。
对于间接函数调用位置或者外部函数调用位置,其标识可以采用固定位数的二进制表示,即可以分配预定长度的二进制长度来进行值标识,例如在一般情况下可以采用16位长度的二进制来表示值标识间接函数调用位置或者外部函数调用位置,这可以识别65535个位置,足以满足绝大多数程序针对间接函数调用和外部函数调用的要求。
本申请实施例中,通过巧妙的利用函数调用位置与函数调用返回地址之间的对应关系,并且基于函数调用位置预先可知的特性,进而可以为每个函数调用位置分配一个不同的值标识,进而可以针对函数调用返回地址这种关键变量,提供了一种快速得到一个程序中每个被调用函数的每个函数调用返回地址的值标识,可以在一定程度上提高值标识的分配效率。另外,还可以根据函数调用方式的不同采用对应不同的值标识分配方式,可以结合各种函数调用方式的调用特点进行对应的值标识的分配,针对性更强,同时针对不同函数调用方式的函数的值标识的分配方式更加灵活。
对于寄存器,例如可以按照以下分配方式进行分配。
每个函数都使用一个固定的寄存器保存其运行过程中的所有函数返回地址的值标识。这需要确定每个函数究竟需要使用哪个寄存器。由于值标识的长度一般比函数调用返回地址本身的长度短,一个寄存器可以存储多个值标识。由于函数调用可能会嵌套,也可能形成递归,在运行时会产生很深的调用栈。随意的寄存器分配方法可能会集中地使用某一个或者几个寄存器,而同时其它寄存器一直空闲。这样会造成频繁的寄存器溢出,导致明显的性能下降。一个好的寄存器分配方案尽力平衡程序运行中用于保护代码指针的寄存器的使用,减少寄存器溢出。为了实现此目标,一种方法是一个程序执行路径上对函数调用返回地址值标识在寄存器之间尽力平均分配。本发明通过在编译过程中构建函数调用图来协助寄存器分配。一个典型的函数调用图的示例如图5所示,函数调用图用于对一个执行路径中的函数按照调用关系进行结构分层,函数调用的返回关系被从图上删除,所有的环被消除掉。构建函数调用图时,直接函数调用的调用位置和被调用函数可以直接识别出来;对于间接函数调用,通过函数调用时的参数类型列表等信息推测可能的被调用函数。这种推测可能是过于乐观的,也就是说有些函数可能在事实上不会被该函数调用位置调用到,这种过度乐观的推测只会影响寄存器分配的效率,并不会对影响方案的逻辑正确性。
构建函数调用图之后,通过为节点分配颜色(color)来确定每个函数分配的寄存器。图上相邻节点分配不同的颜色值,同一层上的没有相互调用关系的函数可以分配相同颜色值。颜色值可以被认为是寄存器的编号,具有相同颜色的函数可以分配使用同一个寄存器。如果颜色数目m大于可用的寄存器的数目n,可以对颜色值进行模n运算后再分配寄存器号。例如如果有5种颜色值,而只有3个可分配寄存器,则颜色值为4和颜色值为1的函数使用相同的寄存器,因为4进行模3的运算后等于1。
由于每个间接函数调用位置可能调用多个目标函数,难以在编译时确定目标函数使用的寄存器。因此本方法对于所有地址采用的函数都分配使用同一个固定的寄存器。前面提到,本方法对外部函数调用视同间接函数调用,本申请实施例也为所有可能被外部调用的函数分配同一个固定的寄存器。
程序执行函数调用时,一个函数返回地址值标识被试图压入该函数使用的寄存器,可以首先检查该寄存器是否有空闲空间,如果没有,则陷入到内核,把所有寄存器的值都保存到安全内存空间,清空所有寄存器(寄存器的值置为0),然后返回到程序继续执行。
当函数调用返回时,可以检查该函数使用的寄存器是否为空(寄存器中的值为0)。如果为空,则从内核内存区域恢复所有寄存器的值,因为该内核内存区域对于应用程序是可读的,所以应用程序无需陷入到内核就可以直接从内核内存区域中读取寄存器溢出值。
该内核内存区域由一个内核模块负责维护,该模块在程序执行之前被加载到内核中。当程序退出时,该内核内存区域被清理并由操作系统回收,同时寄存器中存储的数据也可以清楚。在fork一个进程时,Linux Kernel取消对从父进程继承来的内核内存区域的映射,映射新的内核内存区域并用父进程中的寄存器溢出值对其进行初始化,按照类似的方法,本申请实施例为新创建的线程分配一个新的内核内存区域并用父线程内核内存区域的内容对它进行初始化。
一个函数有可能既被直接调用,也会被间接调用,而本方案对间接函数调用和间接函数调用使用不同寄存器保护其返回地址。一个可行的解决此问题的办法是对所有可能成为间接函数调用目标的函数,对其产生一个函数名不同但参数类型和代码逻辑完全相同的复制,并修改所有的使用该函数的程序位置以使用该复制。这样可以保证程序中原来所有对该函数的间接调用位置现在会调用该复制。类似地,本方法对共享库导出的所有函数都提供复制,并把共享库中所有对这些函数的直接调用都替换成对其复制的调用。
本申请实施例可以在程序中函数调用位置之前和函数返回位置之前插入安全保护的代码,以实现函数返回地址的保护,以下代码示例给出一个可能的对直接函数调用位置进行安全保护的示例代码。该代码示例将要把当前函数调用的返回地址的值标识保存到寄存器%mm0中,这里假设返回地址值标识为4位,从而支持该函数有15个函数调用位置。
1movq%mm0,%rsi
2shrq$60,%rsi
3je no_need_to_spill
4…#trap to kernel
5no_need_to_spill:
6movq%mm0,%rsi
7slq$4,%rsi
8orq%rdi,%rsi
9movq%rsi,%mm0
上述代码示例中的代码行1-3通过测试%mm0寄存器的最高4位是否为0测试该寄存器是否还有可用存储空间。因为0在本方法中被保留而不能用作合法的值标识,如果寄存器最高4位为均0,则说明寄存器还有空间保存当前函数调用返回地址的4位值标识。如果寄存器最高4位均不为0,说明该寄存器的所有空间都已经被占用,已经无法继续左移,此时需要陷入到内核中进行所有寄存器的溢出。代码行6-9把寄存器中现有内容左移4位,把当前函数调用返回地址的4位值标识从尾部压入到寄存器中。
一种可行的对于间接函数调用和外部函数调用返回地址的值标识编码方案是采用16位固定的长度,一共可以表示65535个函数调用位置,可以满足绝大多数程序的要求,本发明实施例用%mm6保存间接函数调用和外部函数调用的返回地址的值标识,由于单个寄存器空间有限,很容易就被占满,为了降低溢出的频率,一种可行的方法是当%mm6被占满时把寄存器内容拷贝到%xmm15寄存器,如果%mm6已满且%xmm15不为空时再把两个寄存器一起溢出到内核内存区域中。
以下代码示例给出一种可行的在函数返回位置之前插入安全保护代码以恢复最初的栈上返回地址的方法。
1movq%mm0,%rdi
2testq%rdi,%rdi
3jne no_need_to_restore
4…#restore registers in user mode
5no_need_to_restore:
6movq%mm0,%rdi
7andq 0xf,%rdi
8movq func1_table(,%rdi,8),%rdi
9movq%rdi,(%rsp)
10movq%mm0,%rdi
11shrq$4,%rdi
12movq%rdi,%mm0
对于上述代码示例,代码行1-3检查%mm0是否为空(也就是寄存器内容是否全为0),如果寄存器为空,则需要从内核内存区域恢复所有寄存器的值,代码行6-9获得寄存器%mm0的最低4位作为检索关键字,通过查询该函数的转换表,获得最初的48位函数返回地址,并把该返回地址写入到栈顶,代码行10-12在弹出%mm0寄存器的最低4位后,将寄存器内容右移4位,调整寄存器中值的格式。
另外,本申请实施例中可以对set jmp/long jmp这两种函数进行的特殊处理。具体来说,在程序调用setjmp函数时,可以让程序陷入内核,并把所有的MMX寄存器以及%xmm15的内容保存到内核内存区域,形成一个setjmp记录,并使用一个以setjmp参数为关键字的Map结构维护所有的setjmp记录。在程序调用longjmp函数时,使用longjmp的参数为检索关键字(key)从Map结构中获取对应的setjmp记录,进而恢复所有的MMX寄存器以及%xmm15的值。
本申请实施例中,对程序中的函数调用位置进行编码,作为函数调用返回地址的值标识,建立用于映射值标识到函数调用返回地址之间的转换表,在函数调用之前,将函数调用返回地址的值标识压入寄存器中,在函数返回之前从寄存器中取出函数调用返回地址对应的值标识并通过转换表转换成对应的函数调用返回地址,进行控制流转移,这使得攻击者无法再通过程序内存安全漏洞篡改函数调用返回地址以实现控制流劫持攻击,提升程序的安全。
本申请实施例的技术方案相对于细粒度CFI技术来说,通过函数调用位置标识,寄存器分配,对应用程序只读的内核内存区域等性能优化技术,实现了较好的函数调用返回地址保护性能。与组粒度CFI技术相比,通过使用寄存器和对用户程序只读的内核内存区域使得攻击者无法对函数调用返回地址进行任何篡改,确保函数返回时总能够返回到正确的函数调用位置,提供了高精度的返回地址保护。与传统CPI技术相比,通过使用寄存器存放返回地址值标识和对用户程序只读的内核内存区域可以抵抗信息泄露攻击,攻击者无法通过泄露存放返回地址的内存区域实现对返回地址的篡改。
二、针对函数指针变量的安全保护。
本申请实施例基于寄存器保护程序中的函数指针变量,函数指针变量主要用于实现间接函数调用,与上述的第一种方案(即针对函数调用返回地址的方案)相比,本申请实施例同样对受保护的对象(即函数指针变量)进行编码得到值标识,并将得到的值标识保存到寄存器中进行保护,与第一种方案相比,本申请实施例采用的值标识的具体编码方式不同,使用的具体寄存器不同,对值标识的检索方法也不同,以下分别说明。
在程序给一个函数指针变量赋值之前或之后,可以对所述函数指针变量的值进行编码得到值标识并把值标识保存到寄存器中,当程序读取所述函数指针变量之前或之后,可以从寄存器中或者寄存器溢出值获得其值标识并转换成值,提供给程序使用。例如,一个函数指针变量pftr,其内存地址为&fptr,其值为fun1(*fptr),本方法采用(&fptr,fun1)作为值标识并保存到寄存器中,即可以将内存地址和值拼接作为其对应的值标识,例如内存地址为0111,而值为1011,那么编码得到的值标识可以是01111011,或者还可以采用其它的处理方式以根据内存地址和值来得到对应的值标识,本申请实施例可以不为函数指针变量在编译时指定固定的寄存器,而采用动态分配寄存器的方法,也就是在需要把函数指针变量的值标识放到寄存器中时,动态从所有用于保护函数指针变量的所有寄存器中选择空闲的寄存器使用。本方案可以不修改程序中原有的对函数指针变量的读操作和写操作,只是在这些操作的位置插入额外指令进行安全保护。
在本申请实施例中,可以识别程序中的函数指针变量读写操作,加入安全保护的代码。对于函数指针变量写操作*fptr=fun1,扫描所有用于保护函数指针变量的寄存器并选择一个有足够空闲空间的寄存器regx,然后把(&fptr,fun1)保存regx中。如果所有寄存器都没有足够空间,则陷入到内核中并把所有用于保护函数指针变量的寄存器溢出到对应用程序只读但对于内核可读可写的内核内存区域。程序中的函数指针变量的读操作出现频率通常要比其写操作频繁的多,出于性能的考虑,本申请实施例提供一种快读路径,即快速查找值标识的方式,具体来说,以&fptr(内存地址)作为关键字直接从内核内存区域(即寄存器的溢出值的保存区域)中检索函数指针变量的值标识。检索到的寄存器溢出值(&fptr,func)被转换成func,这里可以认为是使用了一个隐含的映射(&fptr,func)到func的转换表。由于函数指针变量fptr的某个更加新的值funb有可能已经进入寄存器,所以需要把程序中fptr的值funa与从快速路径中读取并转换得到的值func进行比较,图6中pfr(funa)表示程序中函数指针变量fptr当前的值是funa,(&fptr,funb)和(&fptr,fubc)分别是函数指针变量fptr在寄存器中和在内核内存区域中存储的两个可能不同的值标识。在寄存器中和内核内存区域中出现同一个函数指针变量的不同的值标识是因为该函数指针变量的值标识(&fptr,func)被从寄存器中溢出到了内核内存区域之后,该函数指针变量的值又被修改从而使得其新的值标识进入了寄存器中。如果funa等于func,则直接使用通过快速读路径从寄存器溢出值中读取并转换得到的func作为fptr函数指针变量的值,如果funa不等于func,则本方法放弃通过快速读路径从寄存器溢出值中得到的func,进而遍历所有修改过的寄存器寻找fptr的值标识。如果找到了该值标识(&pftr,funb),则将值标识从寄存器中取出采用funb作为该函数指针变量的值,这里可以认为是使用了一个隐含的映射(&fptr,funb)到funb的转换表。在此过程中,如果从寄存器中取出(&pftr,fun)使得寄存器变为空,则从内核内存区域恢复寄存器的值。如果没有找到,则认为程序中的函数指针变量的值*pftr已经被篡改,进入程序篡改处理(如终止程序)。相当于也有被篡改的可能,这里就是安全保护。从内核内存区域恢复寄存器的值,可以恢复所有用于保存函数指针变量值标识的寄存器,也可以只恢复当前为空的寄存器,本发明优选地使用恢复所有用于函数指针变量值的寄存器,这可以降低恢复的频率,提高性能。在恢复所有用于保存函数返回地址值标识的寄存器时,有些寄存器此时可能并不为空,这时从内核内存区域中恢复足够的内容使得这些寄存器能够填满即可。
从内核内存区域无法检索到给函数指针变量的值标识,则遍历所有寄存器寻找该函数指针变量的值标识。如果找到(&fptr,funb),则转换得到funb作为函数指针变量的值。如果没有找到或寄存器为空,进入程序篡改处理(如终止程序)。
按照本方法提供的快速读路径读取方法,假如程序中一个函数指针变量被篡改成一个该变量以前曾经采用过的值,这种重放攻击的篡改可能无法检测出来,因为此时通过快速读路径读取并转换得到的值可能正好等于该变量以前的值。为了缓解这种可能的重放攻击,本方法在溢出寄存器中的值标识(&fptr,funb)到内核内存区域时,检查funb与fptr函数指针变量在程序中的当前值*fptr是否一致。如果一致,则把(&fptr,funb)溢出到内核内存区域;否则认为*fptr已经被篡改,执行程序篡改处理(如终止程序)。这种处理,实际上把对重放攻击的检测推迟到了函数指针变量的值标识被从寄存器溢出到内核内存区域的时候,以换取函数指针变量读取操作的性能。从理论上说,极端情况下可能出现寄存器中的值标识一直不会溢出到内核内存区域中的情况,但在实际中这种情况出现的概率极低。需要指出的是,之所以可能出现对这种重放攻击检测不出来的情况,是因为为了追求函数指针变量读操作的高性能而对安全功能进行了折中。另一种可行的方法是取消快速读路径而首先遍历所有修改过的寄存器寻以找函数指针变量的值标识。这样可以完全防止到这种重放攻击,不过由于此时没有了快速读路径,读取函数指针变量的操作的性能会有所下降。
寄存器对线程是私有的,如果一个函数指针变量的值被修改并且其值标识进入了寄存器,在寄存器中的值标识溢出到内核内存区域之前,最新的值对于其它线程是不可见的。本方法在线程调用线程同步函数释放一个锁(lock)的时候,可以强制对保存函数指针变量的值标识的寄存器进行溢出。
一种在LLVM(low level virtual machine,一种通用的编译器)中间表示代码层识别程序中对函数指针变量访问的方法是寻找那些带有函数类型的load和store指令,为了防止源代码中的类型信息丢失,本申请实施例可以在O2模式优化之前寻找函数指针变量访问操作。对于可以通过函数调用分析能够直接识别出目标函数的间接调用,本方法在程序中将这种间接调用修改为直接调用,以减少不必要的函数指针变量访问。
程序可能静态初始化全局的函数指针变量,这种初始化不使用store指令。为了解决此问题,本申请实施例可以寻找程序中的全局变量并强制产生对其的store操作。对于memcpy之类的可能隐含函数指针更新的操作,预期程序的开发者会明确标识这些位置并添加store指令。
在本申请实施例,为了提升函数指针保护的性能,可以基于intel MPX(memoryprotection extensions)技术提供寄存器,数据结构和指令设计了一种高效的函数指针变量保护的实现方案。该方案使用MPX的bnd0-bnd3寄存器保存函数指针变量的值标识,使用MPX的radix树作为保存寄存器溢出值的内核内存区域,使用MPX指令实现从内核内存区域快速读取溢出的值标识。MPX的radix树被映射成对用户空间只读而对内核程序可读可写。对于一个函数指针变量fptr,其地址为&fptr,其值为*fptr,其值标识为(&fptr,*pftr)并保存在mpx寄存器中。本方法为了充分利用MPX指令的特点,在溢出MPX寄存器中的值标识到MPX radix树时,将radix节点的32字节块的第一个8字节和第二个8字节都设成*fptr,因为这两个8字节的值在使用MPX界限比较指令时被当做指针的上下界。
1bndmov zero,%bnd0
2bndldx(%rdi),*bnd0
3bndcn,(%rsi),%bnd0
4bndcl(%rsi),%bnd0
上述代码示例给出了一个在程序中的函数指针变量读操作之后插入安全保护指令的例子。%rdi中保存函数指针变量的地址,%rsi保存函数指针变量的值。代码行1清空%bnd0的内容。代码行2以函数指针变量的地址为关键字从radix树中加载溢出到radix树节点的32字节块的前两个8字节(也就是上下界)。如果%rsi与从radix树加载的寄存器溢出值中的函数指针变量的值部分相等,则代码行3和代码行4的检查都会成功。
如果有一个MPX寄存器中保存了该函数指针变量的最新值标识,从而使得radix树中保存的函数指针变量的值标识信息不是最新的,代码行3和代码行4的检查会失败,此时CPU会触发#BR异常。本方案使用的内核模块会处理该异常,如果能在某个MPX寄存器中找到该函数指针变量的值标识,则把所有MPX寄存器的内容保存到radix树中并清空寄存器(置为0),否则进行程序篡改处理。其中,radix树的内存在第一次访问之前并不需要分配实际的物理内存页。此外,程序中含有函数指针变量的内存页数目一般都是非常有限的,这都使得radix不会占用很多的物理内存。
本申请实施例中,函数指针变量的读操作和写操作并对通过插入安全加固指令的方式对函数指针变量进行安全保护,对于写操作插入指令为函数指针变量产生的值标识并保存到寄存器中,对于写操作插入安全指令从寄存器或者寄存器的溢出值中读取值标识并转换得到函数指针变量的值,提供给程序使用,这避免攻击者利用内存安全漏洞篡改程序中函数指针变量的值,实现了函数指针变量的保护。
本申请实施例的技术方案,与细粒度CFI技术相比,通过使用寄存器保护函数指针变量,快读路径等技术,实现了更好的函数指针变量保护性能。与组粒度CFI相比,通过使用寄存器和对用户程序只读的内核内存区域使得攻击者无法对函数指针变量进行篡改,确保函数调用时总能够转移到正确的位置,提供了高精度的函数指针变量保护。与传统CPI技术相比,通过使用寄存器存放函数指针变量的值标识和对用户程序只读的内核内存区域可以抵抗信息泄露攻击,攻击者无法通过泄露存放溢出的值标识的内核内存区域实现对函数指针变量的篡改。
三、针对C++虚函数表指针的安全保护。
每个使用虚函数的C++对象都包含一个指向其使用的虚函数表的指针,这个指针通过其在C++对象中位置和类型可以准确识别出来。在这个意义上,C++对象虚函数表指针可以采用实施例二中提供的保护函数指针变量的方法进行保护。每个c++虚函数表指针vptr本身都有一个地址&vptr,其值为*vptr,本方法采用(&vptr,*vptr)作为其值标识。本方法同样为c++虚函数表指针动态分配mpx寄存器,在寄存器没有足够空闲空间时溢出到一个对用户程序只读对内核程序可读可写的内核内存区域。
本申请实施例与前述的第二种方案(即针对函数指针变量进行保护的方案)的实施原理基本相同,不同之处主要在于两处。第一,程序在读取C++对象虚函数表指针之前,本方案直接检查MPX寄存器,从寄存器中寻找其值标识并进行转换得到虚函数表指针的值,如果找不到再从内核内存区域中寻找,而不是使用前述的快读路径从内核内存区域中读取,是因为C++程序经常在初始化一个对象后立即调用其虚函数,先从寄存器中寻找值标识的方法效率更高,可以取得更好的性能。另外一个不同之处本方法在于对于C++异常处理需要进行特别的加固保护,在C++异常处理时,抛出异常的函数与进行异常处理的函数之前的所有函数调用栈帧(stack frames)都被销毁,在Linux下,Libunwind库识别所有需要销毁的栈帧,并执行每个函数的清理代码,在异常处理过程中,栈上的函数返回地址被Libunwind用于识别上级调用函数,本方案为每个函数中插入代码负责弹出值标识并转换成最初的值并用于替换栈上的返回地址,这样本方案确保了C++异常处理过程中栈上函数返回地址的安全性。
在方法基于LLVM的实现中,在中间表示代码层,如果一个函数需要在异常处理过程中销毁一个在栈上分配的对象,则使用一个清理指令块。该质量块可以通过landingpad指令中的cleanup语句(clause)进行识别,该块的最后一条指令一般是resume指令。本方法在函数的清理指令块中的resume指令之前插入所有安全保护的指令。如果一个函数没有提供该清理指令块,本方法为其添加一个以resume结尾的清理指令块,并在resume之前插入安全保护的指令。本方法会把程序中的所有call指令替换成invoke指令,该指令可以帮助记录异常处理信息。
本申请实施例中的对于内核内存区域的实现可以采用与第二种方案中的MPXRadix树的实现方式,为了简洁,不再重复说明了。
本申请实施例中,采用与第二种方案中类似对函数指针变量的保护方法,防止攻击者利用内存安全漏洞篡改程序中C++对象的虚函数表指针,引起控制流劫持攻击。相对于现有技术来说,细粒度CFI和组粒度CFI都不能检测和防御C++对象的虚函数表指针篡改,而本申请实施例正是基于C++对象的虚函数表指针的保护,与传统CPI技术相比,通过使用寄存器存放虚函数表指针值标识和对用户程序只读的内核内存区域可以抵抗信息泄露攻击,攻击者无法通过泄露存放寄存器溢出值的内核内存区域实现对C++对象虚函数表指针的篡改。
四、针对代码位置指针的安全保护。
本申请实施例采用与前述第二种实施例类型的方案对代码位置指针进行安全保护,具体实施可以参见前述的第二种方案的说明。
需要保护的间接跳转指令在程序中出现的概率很低,程序中switch结构有时候会被编译成一个基于跳表(jump table)的间接jmp指令,但跳表本身是只读的,且访问跳表总是带有界限检查,这样的间接jmp一般无法被攻击者利用程序内存安全漏洞篡改,从而无需额外保护。有些编译器缺省会把间接尾部调用(indirect tail cal)实现成间接jmp指令,但正如第二种方案中所述,可以取消尾部调用优化(tail call optimization)。
对于确实需要进行保护的代码位置指针,可以采用第二种方案中的对函数指针变量的保护方法,该方法中可以不利用函数指针变量一定指向某个函数开始的取值特性,所以其可以直接用于保护程序中的代码位置指针。
对于代码位置指针cptr,其本身的地址为&cptr,其值为*cptr,其值标识则可以为(&cptr,*cptr)。本方法同样为代码位置指针动态分配mpx寄存器以保存值标识,在寄存器没有足够空闲空间把值标识溢出到一个对用户程序只读对内核程序可读可写的内核内存区域。本方法识别程序中的代码位置指针读写操作,加入安全保护的代码。对于代码位置指针写操作*fptr=p,本方法扫描所有用于保护代码位置指针的寄存器并选择一个有足够空闲空间的寄存器regx,然后把(&fptr,p)保存regx中。如果所有寄存器都没有足够空间,则陷入到内核中并把所有用于保护代码位置指针的寄存器溢出(spill)到对应用程序只读但对于内核可读可写的内核内存区域。程序中代码位置指针读操作出现频率通常要比代码位置指针写操作频率高的多,出于性能的考虑,本方法提供一个与第二种方案中介绍的快读路径以&fptr作为关键字直接从内核内存区域中读取代码位置指针的值,具体的检索方式可以参见前述第二种方案的介绍,以及对于内核内存区域的实现可以采用与第二种方案中的MPX Radix树的实现方式,为了简洁,此处不再重复说明了。
本申请实施例中,采用与第二种方案中类似对函数指针变量的保护方法,防止攻击者利用内存安全漏洞篡改程序中的代码位置指针,可以抵御JOP等控制流劫持攻击。相对于现有技术来说,细粒度CFI和组粒度CFI都不能有效防御代码位置指针篡改,因为代码位置指针从理论上说可以指向程序中的任意代码位置。与传统CPI技术相比,通过使用寄存器存放代码位置指针值标识和对用户程序只读的内核内存区域可以抵抗信息泄露攻击,攻击者无法通过泄露存放溢出的值标识的内核内存区域实现对代码位置指针的篡改。
五、针对重要数据变量的安全保护。
申请实施例采用与前述第二种实施例类型的方案对重要数据变量进行安全保护,具体实施可以参见前述的第二种方案的说明。不同之处仅在于保护的对象不同,第二种方案中保护函数指针变量,被申请实施例中保护的是重要数据变量,另外一个不同之处是值标识的检索方式,具体见后文介绍说明。
对于程序中可以影响代码逻辑的重要数据变量,可以通过第二种方案中基于寄存器保护函数指针变量类似的方法,函数指针变量本身就可以认为是一种特殊的重要数据变量,而第二种方案中的方法并没有利用程序中指针独有的特性,所以可直接用于保护程序重要数据变量,对重要数据变量的保护,可以使用MPX寄存器保存其值标识。
一个重要数据变量var,在程序内存中占据一个地址&var,该重要数据变量的值是var’。其值标识可以为(&var,var’),可以为需要保护的重要数据变量动态分配寄存器,以存放其最新的值标识。当程序写var变量时,把要赋予该变量的值var’转换得到值标识(&var,var’)并存放到寄存器中,当程序读变量var时,从寄存器中以&var为关键字寻找其值标识,然后从值标识中提取最初的变量值,提供给程序使用。如果寄存器中没有,则从内核内存区域中检索其值标识。与前述的第二种方案的不同之处在于,检索值标识时首先从寄存器中并进行转换得到重要数据变量的值,如果找不到再从内核内存区域中寻找,而不是首先使用快读路径从内核内存区域中读取,是因为程序可能在对数据变量赋值之后迅速读取其值,首先从寄存器中检索值标识,可以取得更好的性能。
申请实施例中的对于内核内存区域的实现可以采用与第二种方案中的MPX Radix树的实现方式,为了简洁,不再重复说明了。
本申请实施例中,采用与第二种方案中类似对函数指针变量的保护方法,实现了重要数据变量的保护,可以防止攻击者利用内存安全漏洞篡改程序中的重要数据变量,从而抵御数据攻击。现有的细粒度CFI和组粒度CFI都不能有效防御数据攻击,因为数据攻击不涉及到间接跳转指令目标地址的篡改,与DFI技术相比,通过基于寄存器的重要变量保护,快速读路径等技术确保了更好的防御数据攻击的性能。
上述针对集中具体的关键变量的保护进行了介绍,可以理解的是,以上只是以几种具体的关键变量进行举例说明,在具体实施过程中,与上述各个关键变量具有相同的特征的其它关键变量,也可应采用对应的保护机制对相应的关键变量进行对应的安全保护,从而全面地对程序进行安全保护。
本申请实施例中,识别程序中关键变量的读操作和写操作并对通过插入安全加固指令的方式对关键数据进行安全保护,对于写操作插入指令为关键数据产生值标识并保存到寄存器中;对于写读操作插入安全指令从寄存器或者寄存器的溢出值中读取值标识并转换得到关键数据的值,提供给程序使用。函数返回地址的值标识是程序中函数调用位置的整数编码,可以降低寄存器空间占用,并可通过只读的转换表转换成关键数据的值。函数指针变量,代码位置指针,C++对象虚函数表指针和程序重要数据变量的值标识中包含这些数据本身的地址和值,以便于检索和转换。当把关键数据值标识保存到某个寄存器中而寄存器有没有空闲空间时,将寄存器内容溢出到一段对内核可读可写但对应用只读的内核内存区域,从而使得寄存器空闲可用。当寄存器为空时,可以从内核内存区域恢复寄存器的内容,在寄存器中填充可以被检索的值标识。
本申请实施例通过保护代码指针和重要数据变量,可以保护程序中的代码指针不会因为程序中的内存安全漏洞而被篡改以防御控制流劫持攻击,又可以保护程序中的重要数据变量不会因为程序的内存安全漏洞而被篡改以抵御数据攻击同时提供使用寄存器保护关键数据,使用对应用程序只读而对内核可读可写的内存区域保存寄存器溢出值,不依赖于内存区域位置的机密性,能够抵抗信息泄露攻击。并且,通过使用寄存器存储关键变量的值标识,通过内核内存区域保存寄存器溢出值,通过转换表转换值标识到关键数据值,使得代码指针和重要数据变量等关键变量不可篡改和替换,提供高精度的代码指针完整性保护,确保控制流转移时转移到正确的目标地址,同时也可以有效防止数据攻击。
基于同一发明构思,请参见图7,本申请实施例提供一种数据处理装置700,该数据传输装置700可以应用于存储系统,或者是存储系统中的装置,能够实现本申请实施例提供的数据处理方法中存储系统的功能;数据处理装置700也可以是能够支持存储系统实现本申请实施例提供的数据处理方法中存储系统的功能的装置。数据处理装置700可以是硬件结构、软件模块、或硬件结构加软件模块。数据处理装置700可以由芯片系统实现。本申请实施例中,芯片系统可以由芯片构成,也可以包含芯片和其他分立器件。如图7所示,本申请实施例中的数据处理装700可以包括第一确定模块701、第二确定模块702和第三确定模块703。其中:
第一确定模块701,用于在需要读取程序中的关键变量的值时,从安全存储区域中确定与关键变量对应的值标识,其中,关键变量为能够影响程序的执行逻辑的程序变量,值标识是根据关键变量的赋值确定的,安全存储区域包括处理器中的寄存器和内核内存区域,内核内存区域具有对内核可读可写且对应用只读的属性;
第二确定模块702,用于从安全存储区域中读取值标识,并确定与值标识对应的值;
第三确定模块703,用于将值标识对应的值确定为关键变量的值。
在一种可能的实施方式中,关键变量包括函数调用返回地址或函数指针变量或C++对象虚函数表指针或代码位置指针或重要数据变量中的任意一种,其中,重要数据变量包括能够影响程序的执行逻辑的函数参数或全局变量。
在一种可能的实施方式中,本申请实施例中的数据处理装置700还包括存储模块704,由于该存储模块704并不是必须的模块,所以在图7中以虚框标识,存储模块704用于获得关键变量的赋值;通过与关键变量的变量类型对应的转换规则对赋值进行转换处理,以得到与赋值对应的值标识;并将值标识存储到寄存器中。
在一种可能的实施方式中,存储模块704还用于将赋值、值标识和赋值与值标识之间的对应关系存储到转换表中。
在一种可能的实施方式中,存储模块704用于确定寄存器的空闲位是否能够存储值标识;若不能够存储值标识,则将寄存器中存储的其它值标识溢出到内核内存区域中;以及将值标识存储到溢出其它值标识后的寄存器中。
在一种可能的实施方式中,在关键变量为函数调用返回地址时,存储模块704用于将被调用函数的函数调用位置确定为被调用函数的函数调用返回地址;根据被调用函数的调用方式对应的值标识分配方式,为每个函数调用位置分配对应的一个值标识,以得到每个函数调用位置对应的值标识,其中,关键变量当前的函数调用位置对应的值标识为赋值对应的值标识。
在一种可能的实施方式中,在被调用函数的调用方式为直接函数调用时,存储模块704用于确定被调用函数的所有函数调用位置;根据所有函数调用位置的数量,确定用于表示值标识的字段长度,其中,值标识的字段长度能够表示所有函数调用位置的数量;并通过确定的值标识的字段长度,用不同的二进制字段标识不同的函数调用位置,以获得每个函数调用位置的值标识。
在一种可能的实施方式中,程序中的每个直接函数调用的被调用函数对应配置一个寄存器以存储该被调用函数的值标识。
在一种可能的实施方式中,在被调用函数的调用方式为间接函数调用或者外部函数调用时,存储模块704用于通过预定字段长度的值标识方式,用不同的二进制字段表示不同的函数调用位置,以获得每个函数调用位置的值标识。
在一种可能的实施方式中,程序中的所有间接函数调用或者外部函数调用的被调用函数配置一个寄存器组,以通过配置的寄存器组存储所有被调用函数的值标识,其中,寄存器组包括一个或者多个寄存器。
在一种可能的实施方式中,关键变量为函数指针变量或C++对象虚函数表指针或代码位置指针或重要数据变量,存储模块704用于根据关键变量的内存地址和值,确定关键变量的赋值对应的值标识。
在一种可能的实施方式,第一确定模块701用于以关键变量的内存地址作为关键字从安全存储区域中进行检索;并将检索到的包括内存地址的值标识确定为关键变量对应的值标识。
在一种可能的实施方式中,在关键变量为函数指针变量或代码位置指针时,第一确定模块701用于以内存地址作为关键字在内核内存区域中进行检索,以确定在内核内存区域中是否有包括内存地址的值标识;若有,则将包括内存地址的值标识确定为关键变量对应的值标识;若没有,则以内存地址作为关键字在寄存器中进行检索,以将寄存器中包括内存地址的值标识确定为关键变量对应的值标识。
在一种可能的实施方式中,第一确定模块701还用于若有,确定内核内存区域中包括内存地址的值标识所包括的值与关键变量当前在程序中的值是否相同;若相同,则将内核内存区域中包括内存地址的值标识确定为关键变量对应的值标识;若不相同,则在寄存器中以内存地址作为关键字进行检索,以将寄存器中包括内存地址的值标识确定为关键变量对应的值标识。
在一种可能的实施方式中,存储模块704还用于从寄存器的待溢出的值标识中,以内存地址作为关键字进行检索,以确定包括内存地址的目标值标识;确定目标值标识包括的值与关键变量当前在程序中的值是否相同;若相同,则将目标值标识溢出到内核内存区域中;若不相同,则对程序执行程序篡改处理。
在一种可能的实施方式中,在关键变量为C++对象虚函数表指针或重要数据变量时,第一确定模块701用于以内存地址作为关键字在寄存器中进行检索,以确定在寄存器中是否有包括内存地址的值标识;若有,则将包括内存地址的值标识确定为关键变量对应的值标识;若没有,则以内存地址作为关键字在内核内存区域中进行检索,以将内核内存区域中包括内存地址的值标识确定为关键变量对应的值标识。
其中,上述数据处理方法实施例涉及的各步骤的所有相关内容均可以援引到对应功能模块的功能描述,在此不再赘述。
本申请实施例中对模块的划分是示意性的,仅仅为一种逻辑功能划分,实际实现时可以有另外的划分方式,另外,在本申请各个实施例中的各功能模块可以集成在一个处理器中,也可以是单独物理存在,也可以两个或两个以上模块集成在一个模块中。上述集成的模块既可以采用硬件的形式实现,也可以采用软件功能模块的形式实现。
基于同一发明构思,基于同一构思,请参见图8,本申请实施例提供了一种数据处理装置800,图8示出了本申请实施例提供的数据处理装置800的结构示意图,数据处理装置800可以是例如图1所示的计算设备,数据处理装置800可以是终端设备,能够实现本申请实施例提供的数据处理方法的功能;数据处理装置80也可以是能够支持计算设备或者终端设备实现本申请实施例提供的数据处理方法的功能的装置。其中,该数据处理装置80可以为芯片系统,该芯片系统可以由芯片构成,也可以包含芯片和其他分立器件。
数据处理装置800包括至少一个处理器801,用于实现或用于支持数据处理装置800实现本申请实施例图2所示的实施例中的功能。
数据处理装置800还可以包括至少一个存储器802,用于存储程序指令和/或数据。存储器802和处理器801耦合。本申请实施例中的耦合是装置、单元或模块之间的间接耦合或通信连接,可以是电性,机械或其它的形式,用于装置、单元或模块之间的信息交互。处理器801可能和存储器802协同操作。处理器801可能执行存储器802中存储的程序指令。所述至少一个存储器中的至少一个可以包括于处理器中。当处理器801执行存储器802中的程序指令时,可以实现图2所示的数据处理方法。
数据处理装置800还可以包括通信接口803,用于通过传输介质和其它设备进行通信,从而用于数据处理装置800和其它设备进行通信,处理器801可以利用通信接口803收发数据。
本申请实施例中不限定上述通信接口803、处理器801以及存储器802之间的具体连接介质。本申请实施例在图8中以存储器802、处理器801以及通信接口803之间通过总线连接,总线在图8中以粗线表示,其它部件之间的连接方式,仅是进行示意性说明,并不引以为限。所述总线可以分为地址总线、数据总线、控制总线等。为便于表示,图8中仅用一条粗线表示,但并不表示仅有一根总线或一种类型的总线。
在本申请实施例中,处理器801可以是通用处理器、数字信号处理器、专用集成电路、现场可编程门阵列或者其他可编程逻辑器件、分立门或者晶体管逻辑器件、分立硬件组件,可以实现或者执行本申请实施例中的公开的各方法、步骤及逻辑框图。通用处理器可以是微处理器或者任何常规的处理器等。结合本申请实施例所公开的方法的步骤可以直接体现为硬件处理器执行完成,或者用处理器中的硬件及软件模块组合执行完成。
在本申请实施例中,存储器802可以是非易失性存储器,比如硬盘(hard diskdrive,HDD)或固态硬盘(solid-state drive,SSD)等,还可以是易失性存储器(volatilememory),例如随机存取存储器(random-access memory,RAM)。存储器是能够用于携带或存储具有指令或数据结构形式的期望的程序代码并能够由计算机存取的任何其他介质,但不限于此。本申请实施例中的存储器还可以是电路或者其它任意能够实现存储功能的装置,用于存储程序指令和/或数据。
本申请实施例中还提供一种计算机可读存储介质,该计算机可读存储介质包括指令,当其在计算机上运行时,使得计算机执行本申请实施例中的数据处理方法。
本申请实施例还提供了一种芯片系统,该芯片系统包括处理器,还可以包括存储器,用于实现本申请实施例中的数据处理方法。该芯片系统可以由芯片构成,也可以包含芯片和其他分立器件。
本申请实施例还提供一种包含指令的计算机程序产品,所述计算机程序产品中存储有指令,当其在计算机上运行时,使得计算机执行上述数据处理方法。
在上述实施例中,可以全部或部分地通过软件、硬件、固件或者其任意组合来实现、当使用软件程序实现时,可以全部或部分地以计算机程序产品的形式实现。计算机程序产品包括一个或多个指令。在计算机上加载和执行计算机程序指令时,全部或部分地产生按照本申请实施例的流程或功能。计算机可以是通用计算机、专用计算机、计算机网络、或者其他可编程设备。指令可以存储在计算机存储介质中,或者从一个计算机存储介质向另一个计算机存储介质传输,例如,指令可以从一个网站站点、计算机、服务器或数据中心通过有线(例如同轴电缆、光纤、数字用户线(DSL))或无线(例如红外、无线、微波等)方式向另一个网站站点、计算机、服务器或数据中心进行传输。计算机存储介质可以是计算机能够存取的任何可用介质或者是包含一个或多个可用介质集成的服务器、数据中心等数据存储设备。可用介质可以是磁性介质,(例如,软盘、硬盘、磁带、磁光盘(MO)等)、光介质(例如,CD、DVD、BD、HVD等)、或者半导体介质(例如ROM、EPROM、EEPROM、非易失性存储器(NAND FLASH)、固态硬盘(Solid State Disk,SSD)等。
本申请实施例是参照根据本申请实施例的方法、设备(系统)、和计算机程序产品的流程图和/或方框图来描述的。应理解可由指令实现流程图和/或方框图中的每一流程和/或方框、以及流程图和/或方框图中的流程和/或方框的结合。可提供这些指令到通用计算机、专用计算机、嵌入式处理机或其他可编程数据处理设备的处理器以产生一个机器,使得通过计算机或其他可编程数据处理设备的处理器执行的指令产生用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的设备。
这些指令也可存储在能引导计算机或其他可编程数据处理设备以特定方式工作的计算机可读存储器中,使得存储在该计算机可读存储器中的指令产生包括指令设备的制造品,该指令设备实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能。
这些指令也可装载到计算机或其他可编程数据处理设备上,使得在计算机或其他可编程设备上执行一系列操作步骤以产生计算机实现的处理,从而在计算机或其他可编程设备上执行的指令提供用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的步骤。
显然,本领域的技术人员可以对本申请实施例进行各种改动和变型而不脱离本申请的范围。这样,倘若本申请实施例的这些修改和变型属于本申请权利要求及其等同技术的范围之内,则本申请也意图包含这些改动和变型在内。
以上所述,以上实施例仅用以对本申请的技术方案进行了详细介绍,但以上实施例的说明只是用于帮助理解本发明实施例的方法,不应理解为对本发明实施例的限制。本技术领域的技术人员可轻易想到的变化或替换,都应涵盖在本发明实施例的保护范围之内。
Claims (29)
1.一种数据处理方法,其特征在于,所述方法包括:
通过与关键变量的变量类型对应的转换规则对赋值进行转换处理,以得到与所述赋值对应的值标识,其中,通过与关键变量的变量类型对应的转换规则对所述赋值进行转换处理,以得到与所述赋值对应的值标识,包括:将被调用函数的函数调用位置确定为所述被调用函数的函数调用返回地址,并根据所述被调用函数的调用方式对应的值标识分配方式,为每个函数调用位置分配对应的一个值标识,以得到每个函数调用位置对应的值标识,其中,所述关键变量当前的函数调用位置对应的值标识为所述关键变量的赋值对应的值标识,所述关键变量为函数调用返回地址,或者,根据关键变量的内存地址和值,确定所述关键变量的赋值对应的值标识,其中,所述关键变量为函数指针变量或C++对象虚函数表指针或代码位置指针或重要数据变量,所述重要数据变量包括能够影响程序的执行逻辑的函数参数或全局变量;
在需要读取程序中的关键变量的值时,从安全存储区域中确定与所述关键变量对应的值标识,其中,所述关键变量为能够影响所述程序的执行逻辑的程序变量,所述值标识是根据所述关键变量的赋值确定的,所述安全存储区域包括处理器中的寄存器和内核内存区域,所述内核内存区域具有对内核可读可写且对应用只读的属性;
从所述安全存储区域中读取所述值标识,并确定与所述值标识对应的值;
将所述值标识对应的值确定为所述关键变量的值。
2.如权利要求1所述的方法,其特征在于,在从安全存储区域中确定与所述关键变量对应的值标识之前,所述方法还包括:
将所述值标识存储到所述寄存器中。
3.如权利要求2所述的方法,其特征在于,在得到与所述赋值对应的所述值标识之后,所述方法还包括:
将所述赋值、所述值标识和所述赋值与所述值标识之间的对应关系存储到转换表中。
4.如权利要求2所述的方法,其特征在于,将所述值标识存储到所述寄存器中,包括:
确定所述寄存器的空闲位是否能够存储所述值标识;
若不能够存储所述值标识,则将所述寄存器中存储的其它值标识溢出到所述内核内存区域中;
将所述值标识存储到溢出其它值标识后的寄存器中。
5.如权利要求2所述的方法,其特征在于,根据所述被调用函数的调用方式对应的值标识分配方式,为每个函数调用位置分配对应的一个值标识,包括:
在所述被调用函数的调用方式为直接函数调用时,确定所述被调用函数的所有函数调用位置;
根据所述所有函数调用位置的数量,确定用于表示值标识的字段长度,其中,值标识的字段长度能够表示所述所有函数调用位置的数量;
通过确定的值标识的字段长度,用不同的二进制字段标识不同的函数调用位置,以获得每个函数调用位置的值标识。
6.如权利要求5所述的方法,其特征在于,所述程序中的每个直接函数调用的被调用函数对应配置一个寄存器以存储该被调用函数的值标识。
7.如权利要求3所述的方法,其特征在于,根据所述被调用函数的调用方式对应的值标识分配方式,为每个函数调用位置分配对应的一个值标识,包括:
在所述被调用函数的调用方式为间接函数调用或者外部函数调用时,通过预定字段长度的值标识方式,用不同的二进制字段表示不同的函数调用位置,以获得每个函数调用位置的值标识。
8.如权利要求7所述的方法,其特征在于,所述程序中的所有间接函数调用或者外部函数调用的被调用函数配置一个寄存器组,以通过配置的寄存器组存储所有被调用函数的值标识,其中,所述寄存器组包括一个或者多个寄存器。
9.如权利要求3所述的方法,其特征在于,从安全存储区域中确定与所述关键变量对应的值标识,包括:
以所述关键变量的内存地址作为关键字从所述安全存储区域中进行检索;
将检索到的包括所述内存地址的值标识确定为所述关键变量对应的值标识。
10.如权利要求9所述的方法,其特征在于,所述关键变量为函数指针变量或代码位置指针,以所述关键变量的内存地址作为关键字从所述安全存储区域中进行检索,包括:
以所述内存地址作为关键字在所述内核内存区域中进行检索,以确定在所述内核内存区域中是否有包括所述内存地址的值标识;
若有,则将包括所述内存地址的值标识确定为所述关键变量对应的值标识;
若没有,则以所述内存地址作为关键字在所述寄存器中进行检索,以将所述寄存器中包括所述内存地址的值标识确定为所述关键变量对应的值标识。
11.如权利要求10所述的方法,其特征在于,所述方法还包括:
若有,确定所述内核内存区域中包括所述内存地址的值标识所包括的值与所述关键变量当前在所述程序中的值是否相同;
若相同,则将所述内核内存区域中包括所述内存地址的值标识确定为所述关键变量对应的值标识;
若不相同,则在所述寄存器中以所述内存地址作为关键字进行检索,以将所述寄存器中包括所述内存地址的值标识确定为所述关键变量对应的值标识。
12.如权利要求10或11所述的方法,其特征在于,所述方法还包括:
从所述寄存器的待溢出的值标识中,以所述内存地址作为关键字进行检索,以确定包括所述内存地址的目标值标识;
确定所述目标值标识包括的值与所述关键变量当前在所述程序中的值是否相同;
若相同,则将所述目标值标识溢出到所述内核内存区域中;
若不相同,则对所述程序执行程序篡改处理。
13.如权利要求9所述的方法,其特征在于,所述关键变量为C++对象虚函数表指针或重要数据变量,以所述关键变量的内存地址作为关键字从所述安全存储区域中进行检索,包括:
以所述内存地址作为关键字在所述寄存器中进行检索,以确定在所述寄存器中是否有包括所述内存地址的值标识;
若有,则将包括所述内存地址的值标识确定为所述关键变量对应的值标识;
若没有,则以所述内存地址作为关键字在所述内核内存区域中进行检索,以将所述内核内存区域中包括所述内存地址的值标识确定为所述关键变量对应的值标识。
14.一种数据处理装置,其特征在于,所述装置包括:
存储模块,用于通过与关键变量的变量类型对应的转换规则对赋值进行转换处理,以得到与所述赋值对应的值标识,其中,通过与关键变量的变量类型对应的转换规则对所述赋值进行转换处理,以得到与所述赋值对应的值标识,包括:将被调用函数的函数调用位置确定为所述被调用函数的函数调用返回地址,并根据所述被调用函数的调用方式对应的值标识分配方式,为每个函数调用位置分配对应的一个值标识,以得到每个函数调用位置对应的值标识,其中,所述关键变量当前的函数调用位置对应的值标识为所述关键变量的赋值对应的值标识,所述关键变量为函数调用返回地址,或者,根据关键变量的内存地址和值,确定所述关键变量的赋值对应的值标识,其中,所述关键变量为函数指针变量或C++对象虚函数表指针或代码位置指针或重要数据变量,所述重要数据变量包括能够影响程序的执行逻辑的函数参数或全局变量;
第一确定模块,用于在需要读取程序中的关键变量的值时,从安全存储区域中确定与所述关键变量对应的值标识,其中,所述关键变量为能够影响所述程序的执行逻辑的程序变量,所述值标识是根据所述关键变量的赋值确定的,所述安全存储区域包括处理器中的寄存器和内核内存区域,所述内核内存区域具有对内核可读可写且对应用只读的属性;
第二确定模块,用于从所述安全存储区域中读取所述值标识,并确定与所述值标识对应的值;
第三确定模块,用于将所述值标识对应的值确定为所述关键变量的值。
15.如权利要求14所述的装置,其特征在于,所述存储模块还用于:
将所述值标识存储到所述寄存器中。
16.如权利要求15所述的装置,其特征在于,所述存储模块还用于:
将所述赋值、所述值标识和所述赋值与所述值标识之间的对应关系存储到转换表中。
17.如权利要求15所述的装置,其特征在于,所述存储模块用于:
确定所述寄存器的空闲位是否能够存储所述值标识;
若不能够存储所述值标识,则将所述寄存器中存储的其它值标识溢出到所述内核内存区域中;
将所述值标识存储到溢出其它值标识后的寄存器中。
18.如权利要求15所述的装置,其特征在于,所述存储模块用于:
在所述被调用函数的调用方式为直接函数调用时,确定所述被调用函数的所有函数调用位置;
根据所述所有函数调用位置的数量,确定用于表示值标识的字段长度,其中,值标识的字段长度能够表示所述所有函数调用位置的数量;
通过确定的值标识的字段长度,用不同的二进制字段标识不同的函数调用位置,以获得每个函数调用位置的值标识。
19.如权利要求15所述的装置,其特征在于,所述程序中的每个直接函数调用的被调用函数对应配置一个寄存器以存储该被调用函数的值标识。
20.如权利要求15所述的装置,其特征在于,所述存储模块用于:
在所述被调用函数的调用方式为间接函数调用或者外部函数调用时,通过预定字段长度的值标识方式,用不同的二进制字段表示不同的函数调用位置,以获得每个函数调用位置的值标识。
21.如权利要求20所述的装置,其特征在于,所述程序中的所有间接函数调用或者外部函数调用的被调用函数配置一个寄存器组,以通过配置的寄存器组存储所有被调用函数的值标识,其中,所述寄存器组包括一个或者多个寄存器。
22.如权利要求15所述的装置,其特征在于,所述第一确定模块用于:
以所述关键变量的内存地址作为关键字从所述安全存储区域中进行检索;
将检索到的包括所述内存地址的值标识确定为所述关键变量对应的值标识。
23.如权利要求22所述的装置,其特征在于,所述关键变量为函数指针变量或代码位置指针,所述第一确定模块用于:
以所述内存地址作为关键字在所述内核内存区域中进行检索,以确定在所述内核内存区域中是否有包括所述内存地址的值标识;
若有,则将包括所述内存地址的值标识确定为所述关键变量对应的值标识;
若没有,则以所述内存地址作为关键字在所述寄存器中进行检索,以将所述寄存器中包括所述内存地址的值标识确定为所述关键变量对应的值标识。
24.如权利要求23所述的装置,其特征在于,所述第一确定模块还用于:
若有,确定所述内核内存区域中包括所述内存地址的值标识所包括的值与所述关键变量当前在所述程序中的值是否相同;
若相同,则将所述内核内存区域中包括所述内存地址的值标识确定为所述关键变量对应的值标识;
若不相同,则在所述寄存器中以所述内存地址作为关键字进行检索,以将所述寄存器中包括所述内存地址的值标识确定为所述关键变量对应的值标识。
25.如权利要求23或24所述的装置,其特征在于,所述存储模块还用于:
从所述寄存器的待溢出的值标识中,以所述内存地址作为关键字进行检索,以确定包括所述内存地址的目标值标识;
确定所述目标值标识包括的值与所述关键变量当前在所述程序中的值是否相同;
若相同,则将所述目标值标识溢出到所述内核内存区域中;
若不相同,则对所述程序执行程序篡改处理。
26.如权利要求22所述的装置,其特征在于,所述关键变量为C++对象虚函数表指针或重要数据变量,所述第一确定模块用于:
以所述内存地址作为关键字在所述寄存器中进行检索,以确定在所述寄存器中是否有包括所述内存地址的值标识;
若有,则将包括所述内存地址的值标识确定为所述关键变量对应的值标识;
若没有,则以所述内存地址作为关键字在所述内核内存区域中进行检索,以将所述内核内存区域中包括所述内存地址的值标识确定为所述关键变量对应的值标识。
27.一种数据传输装置,其特征在于,包括处理器及存储器,所述存储器中存储有计算机程序指令,当所述处理器执行所述计算机程序指令时,实现如权利要求1-13中任一项所述的方法。
28.一种计算机可读存储介质,其特征在于,所述计算机可读存储介质上存储有指令,当所述指令在计算机上运行时,使得计算机执行如权利要求1-13中任一项所述的方法。
29.一种计算机程序产品,其特征在于,所述计算机程序产品包含有指令,当所述指令在计算机上运行时,使得所述计算机执行如权利要求1-13中任一项所述的方法。
Priority Applications (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201811651849.XA CN111381879B (zh) | 2018-12-31 | 2018-12-31 | 一种数据处理方法及装置 |
Applications Claiming Priority (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201811651849.XA CN111381879B (zh) | 2018-12-31 | 2018-12-31 | 一种数据处理方法及装置 |
Publications (2)
Publication Number | Publication Date |
---|---|
CN111381879A CN111381879A (zh) | 2020-07-07 |
CN111381879B true CN111381879B (zh) | 2022-09-02 |
Family
ID=71222558
Family Applications (1)
Application Number | Title | Priority Date | Filing Date |
---|---|---|---|
CN201811651849.XA Active CN111381879B (zh) | 2018-12-31 | 2018-12-31 | 一种数据处理方法及装置 |
Country Status (1)
Country | Link |
---|---|
CN (1) | CN111381879B (zh) |
Families Citing this family (9)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN114064302B (zh) * | 2020-07-30 | 2024-05-14 | 华为技术有限公司 | 一种进程间通信的方法及装置 |
CN112035379B (zh) * | 2020-09-09 | 2022-06-14 | 浙江大华技术股份有限公司 | 存储空间的使用方法、装置、存储介质以及电子装置 |
CN112015826B (zh) * | 2020-10-27 | 2021-01-29 | 腾讯科技(深圳)有限公司 | 基于区块链的智能合约安全性检测方法及相关设备 |
CN112486089A (zh) * | 2020-12-10 | 2021-03-12 | 郑州捷安高科股份有限公司 | 可编程逻辑控制器的数据处理方法、装置及计算机设备 |
CN113032737B (zh) * | 2021-03-15 | 2021-11-30 | 清华大学 | 软件的保护方法、装置、电子设备及存储介质 |
CN115220789B (zh) * | 2022-06-24 | 2023-02-07 | 北京联盛德微电子有限责任公司 | 一种用于多寄存器的操作命令触发调度方法及单元 |
CN117555599B (zh) * | 2024-01-10 | 2024-04-05 | 睿思芯科(成都)科技有限公司 | 加快关键数据访问速度的芯片设计方法、系统及相关设备 |
CN117785248B (zh) * | 2024-02-28 | 2024-05-24 | 上海励驰半导体有限公司 | 程序升级中关键变量的注册方法、装置、存储介质及芯片 |
CN118394351B (zh) * | 2024-07-01 | 2024-09-27 | 杭州高新区(滨江)区块链与数据安全研究院 | 智能合约的编译方法、装置、终端设备及存储介质 |
Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN102375957A (zh) * | 2011-11-10 | 2012-03-14 | 西安电子科技大学 | 内核级return-oriented rootkits的防御方法 |
CN104364759A (zh) * | 2012-04-19 | 2015-02-18 | 加泰罗尼亚理工大学 | 用于控制计算机系统上硬件资源使用情况的方法、系统以及可执行代码段 |
Family Cites Families (1)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
US8312468B2 (en) * | 2009-06-09 | 2012-11-13 | Open Kernel Labs | Methods and apparatus for fast context switching in a virtualized system |
-
2018
- 2018-12-31 CN CN201811651849.XA patent/CN111381879B/zh active Active
Patent Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN102375957A (zh) * | 2011-11-10 | 2012-03-14 | 西安电子科技大学 | 内核级return-oriented rootkits的防御方法 |
CN104364759A (zh) * | 2012-04-19 | 2015-02-18 | 加泰罗尼亚理工大学 | 用于控制计算机系统上硬件资源使用情况的方法、系统以及可执行代码段 |
Non-Patent Citations (2)
Title |
---|
基于AMD硬件内存加密机制的关键数据保护方案;吴宇明等;《信息安全学报》;20180115(第01期);第31-47页 * |
基于硬件虚拟化的虚拟机内核完整性保护;杨晓晖等;《河北大学学报(自然科学版)》;20180325(第02期);全文 * |
Also Published As
Publication number | Publication date |
---|---|
CN111381879A (zh) | 2020-07-07 |
Similar Documents
Publication | Publication Date | Title |
---|---|---|
CN111381879B (zh) | 一种数据处理方法及装置 | |
US11720367B2 (en) | Securing conditional speculative instruction execution | |
US8434064B2 (en) | Detecting memory errors using write integrity testing | |
US8352797B2 (en) | Software fault isolation using byte-granularity memory protection | |
KR101183432B1 (ko) | 다수의 필드를 갖는 데이터를 안전하게 하기 위한 방법,시스템 및 컴퓨터-액세스가능 매체 | |
JP6474398B2 (ja) | コードスタック管理 | |
CN114641770B (zh) | 使用页帧标签机制来增强存储器安全编程 | |
JP2022065654A (ja) | 無効なメモリ参照に対する保護のためのシステム、コンピュータ実装方法、およびコンピュータプログラム製品(無効なメモリ参照に対する保護) | |
US20230236925A1 (en) | Tag checking apparatus and method | |
US20210150028A1 (en) | Method of defending against memory sharing-based side-channel attacks by embedding random value in binaries | |
CN115510430A (zh) | 一种函数指针及其数据依赖的识别与保护方法、装置 | |
CN114902178A (zh) | 域转换禁用配置参数 | |
US9639477B2 (en) | Memory corruption prevention system | |
US11500982B2 (en) | Systems and methods for reliably injecting control flow integrity into binaries by tokenizing return addresses | |
CN115422554A (zh) | 请求处理方法、编译方法和可信计算系统 | |
CN112199116B (zh) | 操作数栈寄存器识别方法、装置、设备及存储介质 | |
KR102351663B1 (ko) | Cfi 기반 got 변조 공격 방지 장치 및 그 방법 | |
Roth et al. | Implicit buffer overflow protection using memory segregation | |
US11361070B1 (en) | Protecting devices from remote code execution attacks | |
US11995178B2 (en) | Protection of kernel from code reuse attacks | |
US20240095363A1 (en) | Method, device, and electronic apparatus for securely passing data | |
Kugler et al. | SCADS: Separated Control-and Data-Stacks | |
Karwayun et al. | War of Control Hijacking: Attacks and Defenses | |
Zolotarev et al. | Memory Obfuscation by Stack Randomization for Android Applications | |
WO2022218517A1 (en) | Method and device for verifying execution of a program code |
Legal Events
Date | Code | Title | Description |
---|---|---|---|
PB01 | Publication | ||
PB01 | Publication | ||
SE01 | Entry into force of request for substantive examination | ||
SE01 | Entry into force of request for substantive examination | ||
GR01 | Patent grant | ||
GR01 | Patent grant |