发明内容
有鉴于此,本发明提供了一种保护软件中数据的方法。能够很有效的保护关键数据或代码,使软件在运行中不加载软件保护装置的驱动程序,或加载软件保护装置的驱动程序,但没有挂钩页面异常的情况下,根本无法正常取得关键数据或代码。恢复关键数据的操作也在驱动中完成,对盗版或破解者来说提高了跟踪调试的门槛,从而更为有效的保护了软件,提高了安全性。本发明不仅适用于windows操作***,还适用于Linux等其他操作***。
根据本发明的一个方面,提供一种通过软件保护装置保护软件中数据的方法,包括如下步骤:
步骤1,把受保护软件中需要保护的数据移入所述软件保护装置的驱动程序中;
步骤2,在所述受保护软件使用所述需要保护的数据前,加载所述软件保护装置的所述驱动程序,使之挂钩页面异常;
步骤3,当所述受保护软件需要读、写或执行所述需要保护的数据时,将所述需要保护的数据写回到所述受保护软件中。
根据本发明的一个方面,在步骤1中,在抽取受保护软件中的需要保护的数据之后,记录其地址和长度,将其清0或填入随机数。
根据本发明的一个方面,将所述需要保护的数据进行加密后移入所述软件保护装置的驱动程序中。
根据本发明的一个方面,在所述受保护软件需要读、写或执行所述需要保护的数据前,由所述受保护软件加载所述软件保护装置的驱动程序。
根据本发明的一个方面,由所述驱动程序挂钩操作***的页面异常函数。
根据本发明的一个方面,当所述受保护软件读写、执行所述需要保护的数据时,CPU会触发页面异常函数,并进行判断,
如果判断结果是缺页异常,且缺少页面属于所述受保护软件进程的需要保护的数据地址的范围时,则将保存在所述驱动程序中的所述需要保护的数据解密后恢复,然后返回供所述受保护软件使用;
如果判断结果是页面保护异常,或缺少页面不属于需要保护的数据地址范围,则不做任何处理,执行所述操作***原页面异常函数。
根据本发明的一个方面,所述将取出的需要保护的数据写入所述驱动程序时,将取出的需要保护的数据以数组形式或存入文件的方式写入。
根据本发明的一个方面,所采用的加解密方式采用对称或者非对称算法。
本发明将关键数据或代码写入软件保护装置的驱动程序,载入时,软件保护装置的驱动程序与***的页面异常函数挂钩,当软件读写、执行关键数据时,根据判断,将保存在软件保护装置的驱动程序中的关键数据解密后恢复,返回供受保护软件执行。通过本发明提供的方法,将恢复数据等操作放在软件保护装置的驱动程序中执行,提高了软件跟踪调试的门槛,可以更有效的保护软件。
具体实施方式
为使本发明的目的、技术方案及优点更加清楚明白,以下参照附图并举实施例,对本发明进一步详细说明。
本发明提供一种保护软件数据的方法,具体步骤包括:
步骤1.抽取软件中的关键数据或代码(代码是一种比较特殊的数据),并记录其地址和长度后,将其清0或填入随机数; 根据本发明的一个实施例,关键数据或者代码是一个加密数据的代码,或加密数据代码所用到的数据,也可以是一个重要功能的实现代码;
步骤2.将取出的数据写入软件保护装置的驱动程序中,可加密后存放,根据本发明的一个实施例,既可以存放在存储设备中,也可以直接存放在软件保护装置的驱动程序中;
步骤3.在软件需要读写、执行关键数据或代码前,需要由该软件加载软件保护装置的驱动程序,驱动程序挂钩***的页面异常函数(清掉所在页面PTE(Page Table Entry)和TLB(Translation Lookaside Buffers)入口,以保证CPU能够触发页面异常)。
步骤4:当软件读写、执行关键数据时,CPU会触发页面异常函数,并进行判断,如果判断结果是缺页异常(也可以叫做出错的页面,当缺页异常出现后,要判断这个出错页面中是否包含了保护的数据或代码,如果包含的话,就要特殊处理,因为页面是有序号的,每个序号对应4K连续储存的数据,64位CPU是8K),且缺少页面属于该软件进程的关键数据地址的范围时,则将保存在软件保护装置驱动程序中的关键数据解密后恢复,然后返回供软件使用;如果判断结果是页面保护异常,或缺少页面不属于关键数据地址范围就不做任何处理,执行***原页面异常函数。
根据本发明的一个方面,所述将取出的数据写入驱动程序时,将取出的数据以数组形式或其他形式(如将数据存入文件等方式)写入。
根据本发明的一个方面,驱动程序中的关键数据所采用的加解密方式可采用包括对称(如AES、DES等)或者非对称算法(如RSA、ECC等)。
所述挂钩***页面异常是指可使用CPU特权指令sidt。另外,根据本发明的另一个实施方式,当适用linux操作***时,其对应的CPU特权指令如下:
// 获取中断描述符表寄存器的地址idtr
unsigned char idtr[6];
asm ("sidt %0" : "=m" (idtr));
base = *((unsigned long *) &idtr[2]);
很明显,IDT属于CPU架构,不同***下区别不大。
IDT = Interrupt Descriptor Table 中断描述表
以WINDOWS X86(根据本发明的一个实施方式,此处仅以32位的操作***为例,其它操作***可以进行类似处理)为例:
IDT是一个有256个入口的线形表,每个IDT的入口是个8字节的描述符,所以整个IDT表的大小为256*8=2048 bytes,每个中断向量关联了一个中断处理过程。所谓的中断向量就是把每个中断或者异常用一个0-255的数字识别。Intel称这个数字为向量(vector)。
对于中断描述表,操作***使用IDTR寄存器来记录idt位置和大小。
IDTR寄存器是48位寄存器,用于保存idt信息。其中低16位代表IDT的大小,大小为7FFH,高32位代表IDT的基地址。可以利用指令sidt读出IDTR寄存器中的信息,从而找到IDT在内存中的位置。下文是IDTR的示例性描述。本领域的技术人员对于这种描述都能够很容易的理解,也可以进行相似的变换、修改、替换、增减,其都属于本发明的范围。
// IDTR
#pragma packet(1)
typedef struct _IDTR
{
USHORT limit;
USHORT LowBase;
USHORT HighBase;
}IDTR,*PIDTR;
#pragma packet()
// IDT入口表
#pragma packet(1)
typedef struct _IDT_ENTRY
{
USHORT offset_low;
USHORT selector;
UCHAR reserved;
UCHAR type:4;
UCHAR always0:1;
UCHAR dpl:2;
UCHAR present:1;
USHORT offset_high;
}IDT_ENTRY,*PIDT_ENTRY;
#pragma packet()
得到IDT的地址,IDT整体是一个结构数组,其中第0xE个结构就是页面异常信息结构,里面两个成员OffsetLow和OffsetHigh组成一个地址,就是***的异常处理函数,它处理***中所有的页面异常,将自定义的页面异常过滤函数地址替换进去,也要将***原有页面异常函数的地址保存在驱动中,以便还原操作时使用。多核CPU需要将每个CPU的IDT全部处理。作为一个实例,如下给出给出多核cpu的进行idt处理实例的示例性代码。对于本领域的技术人员而言,以下代码仅作为示例,其不构成对本发明的限制。本领域的技术人员根据如下代码,设计、编写、生成的其他各种形式或者方式,都属于本发明所要求保护的范围之内。
// 对多核CPU的每一个进行挂钩缺页异常处理
CpuCount = *KeNumberProcessors;
while( CpuCount > 0)
{
KeSetAffinityThread( KeGetCurrentThread(), CpuCount);//绑定CPU
// 挂钩缺页异常处理
CpuCount--;
}
参见图1,其为软件运行流程整体示意图。
在步骤101中,把受保护软件中需要保护的数据移入软件保护装置的驱动程序中;
在步骤102中,在受保护软件使用数据前,加载软件保护装置的驱动程序,使之挂钩页面异常;
在步骤103中,当受保护软件需要读、写或执行被保护的数据时,再将原数据写回去。
参见图2,图2为按照本发明的一种保护软件数据的方法的一优选实施例的整体流程示意图。
本实施例假设在Windows x86平台下,有一个软件A,其中有一段关键数据D(只需要读取),软件在响应菜单栏操作时才需要读取关键数据D。
1.先将软件A中的关键数据D取出,加密放入软件保护保护装置的驱动程序B中(其中,加密后的数据,可以当做一组连续的数据存放在驱动程序B中,当做数据来使用),然后将软件A中的关键数据D清0。
2.在软件A调用响应菜单栏操作的开始处载入驱动程序B,然后驱动程序B挂钩_KiTrap0E(注:这个函数是WINDOWS***下缺页异常处理函数(根据本发明的一个实施例,这个函数名_KiTrap0E,仅针对WINDOWS***。在LINUX是其他的名字,但本发明关注点不在于操作***之间的差异,在此从略。)(可通过特权指令sidt取行IDT,第0x0E个项就是_KiTrap0E首地址)。
3.当软件A运行后,点击菜单项,驱动程序B加载,在驱动程序B中计算出关键数据D所在的页面PTE被置为无效;本领域的技术人员对于这种描述都能够很容易的理解,也可以进行相似的变换、修改、替换、增减,其都属于本发明的范围。
// 获取地址所在页面的PTE(此函数的功能概要)
PPTE GetPteAddress( PVOID VirtualAddress )
{
PPTE pPTE = 0;
__asm
{
cli //disable interrupts 禁止中断
pushad
mov esi, PROCESS_PAGE_DIR_BASE
mov edx, VirtualAddress
mov eax, edx
shr eax, 22
lea eax, [esi + eax*4] //pointer to page directory entry 指向页面目录条目的指针
test [eax], 0x80 //is it a large page?是否为大页面?
jnz Is_Large_Page //it's a large page 是大页面?
mov esi, PROCESS_PAGE_TABLE_BASE
shr edx, 12
lea eax, [esi + edx*4] //pointer to page table entry (PTE)指向页面表条目的指针
mov pPTE, eax
jmp Done
//NOTE: There is not a page table for large pages because
//the phys frames are contained in the page directory. 因为在页面目录中存在phys帧,在大页面中不存在页面表
Is_Large_Page:
mov pPTE, eax
Done:
popad
sti //reenable interrupts 重新使能中断
}//end asm
return pPTE;
}//end GetPteAddress
// 将关键数据D的PTE设置为无效
push eax // 关键数据D的地址
call GetPteAddress
mov ebx, eax //ebx = pPte
and dword ptr [ebx], 0xFFFFFFFE //mark page not present,标记不存在页面
4.使用汇编命令invlpg清掉TLB入口,以保证读取时能够触发页面异常(即图2中的“触发页面异常”),然后软件A读取关键数据D(此时软件A中原关键数据D的内容已全部为0),当读取时,触发页面异常,首先会进入挂钩_KiTrap0E的过滤函数进行过滤(触发页面异常后***会进入_KiTrap0E函数,但本发明在之前挂钩了此函数,所以会先进到_KiTrap0E_Filter,处理后如果不是下面这些条件,就再调用***_KiTrap0E函数,否则就解密还原数据后直接返回),用错误码判断条件
a.缺页错误
b.是否为软件A进程
c.读取时出错
d.用户模式下的异常
e.并且读取的地址cr2(寄存器)是在软件A中的关键数据D的地 址范围内。
当符合以上条件时,将保存在驱动程序B中的关键数据解密,直接复制到软件A中的原地址上。此时,关键数据D已完全恢复,软件A可顺利读取到关键数据D。
为安全考虑,可以在不使用关键数据D的时候,将该区域清0,通知驱动程序B,使页面异常挂钩无效,这样就能最大限度保护关键数据D的安全性。
以上所述仅为本发明的较佳实施例而已,并非用于限定本发明的保护范围。凡在本发明的精神和原则之内,所作的任何修改、等同替换以及改进等,均应包含在本发明的保护范围之内。