专利名称:一种运行含有自校验的加壳程序的方法和装置的制作方法
技术领域:
本发明涉及软件保护领域,尤其涉及一种运行含有自校验的加壳程序的方法和装置。
背景技术:
自校验是一种用于在软件运行时检查软件自身是否被修改的方法。例如,CRC(Cyclic Redundancy Check,循环冗余校验码)就是数据通信领域中最常用的一种差错校验码,可以用来判断软件自身是否被破解或者破坏。当软件被修改后,校验出的CRC值就和原来的CRC值不同,从而判断出软件被改动。因为CRC校验的实现简单,相对比较安全,所以这种方法广泛的应用在软件行业。加壳就是对可执行程序进行代码段、数据段加密和压缩,是一种保护可执行文件的常用手段。加壳的目的是阻止对软件的反汇编分析或者动态分析,这种技术常用来保护软件,防止软件被破解。而对于含有自校验的加壳程序,加壳后的软件由于对软件进行加密和压缩,校验出来的值就和以前的不同,因此无法通过自校验。现有技术运行含有自校验的加壳程序的方法是在运行期间,壳把原程序释放到硬盘上,再通过创建新的进程或系统内存加载技术运行原程序。这种技术的缺点是原程序被释放到了硬盘上,原程序可以通过一些工具被找到原程序,在以后的使用过程直接使用原程序即可,加壳技术没有起到保护原程序的作用。
发明内容
本发明的目的是为了克服现有技术的不足,提供一种运行含有自校验的加壳程序的方法和装置,提高了加壳程序的安全性。本发明提供的一种运行含有自校验的加壳程序的方法,包括步骤S1:壳获取指定接口函数的内存地址;步骤S2 :所述壳将所述指定接口函数中的部分指令替换为跳转到对应的钩子函数的跳转指令,将所述指定接口函数的所述部分指令保存到与所述指定接口函数对应的钩子函数的代码存储区中,再加入跳转回对应的所述接口函数的跳转指令,所述壳获取加壳程序在存储设备上的访问路径,将加密后的原程序解密到内存中;步骤S3 :所述壳将控制权交给所述原程序的入口点;步骤S4 :所述钩子函数等待被调用;当第一钩子函数被调用时,所述第一钩子函数调用访问文件函数,返回调用结果,判断所述访问文件函数的位置参数是否与所述加壳程序在所述存储设备上的访问路径一致,若一致则判断所述调用结果是否包含第一句柄,是则保存所述第一句柄,所述第一钩子函数结束,否则所述第一钩子函数结束;若不一致则所述第一钩子函数结束;当第二钩子函数被调用时,所述第二钩子函数读取获取文件大小函数中的句柄参数,判断所述句柄参数是否为保存的所述第一句柄,是则所述第二钩子函数调用获取文件大小函数,获取第一文件大小,对所述第一文件大小进行计算得到第二文件大小,将所述第二文件大小返回给所述原程序,所述第二钩子函数结束;否则,所述第二钩子函数调用所述获取文件大小函数,返回调用结果,所述第二钩子函数结束;当第三钩子函数被调用时,所述第三钩子函数读取读文件函数中的句柄参数,判断所述句柄参数是否为保存的所述第一句柄,是则执行步骤S5,否则,所述第三钩子函数调用所述读文件函数读取相应内容,将读取到的内容返回给所述原程序,所述第三钩子函数结束;当第四钩子函数被调用时,所述第四钩子函数调用创建文件映射函数,返回调用结果,判断创建文件映射函数的句柄参数是否与保存的所述第一句柄一致,若一致则判断所述调用结果是否包含映射文件句柄,是则保存所述映射文件句柄,所述第四钩子函数结束,否则所述第四钩子函数结束;若不一致则所述第四钩子函数结束;当第五钩子函数被调用时,所述第五钩子函数读取所述映射文件函数中的句柄参数,判断所述句柄参数是否为保存的所述映射文件句柄,是则所述第五钩子函数调用所述映射文件函数,根据壳的长度和加密头的大小将所述原程序中的与所述句柄参数对应的内容映射到所述内存,对所述内存中映射到的内容进行解密,将解密后的内容返回给所述原程序,所述第五钩子函数结束;否则,所述第五钩子函数将与所述句柄参数对应的内容映射到所述内存,返回所述映射的内容,所述第五钩子函数结束;步骤S5 :所述第三钩子函数判断将要读取的内容的大小是否在所述加密后的原程序的大小范围内,是则所述第三钩子函数调用读文件函数,根据壳的长度和加密头的大小读取所述原程序中的相应内容,对读取到的内容进行解密,并将解密后的内容返回给所述原程序,所述第三钩子函数结束;否则,所述第三钩子函数调用读文件函数,读取相应内容,对所述读取到的内容进行解密,并将解密后的内容返回给所述原程序,所述第三钩子函数结束。所述步骤SI中所述指定接口函数为所述访问文件函数或所述获取文件大小函数或所述读文件函数或所述创建文件映射函数或所述映射文件函数。所述步骤S2包括步骤S2-1 :所述壳获取所述跳转到对应的钩子函数的指令的长度;步骤S2-2 :所述壳从所述指定接口函数中的第一条指令中解析出完整的汇编指令,并将所述汇编指令的长度作为当前指令长度;步骤S2-3 :所述壳判断当前指令长度是否小于所述跳转到对应的钩子函数的指令的长度,是则执行步骤S2-4,否则执行步骤S2-5 ;步骤S2-4 :所述壳将所述指定接口函数中的下一条指令解析出完整的汇编指令,用所述汇编指令的长度与当前指令长度的和更新当前指令长度,执行步骤S2-3 ;步骤S2-5 :所述壳读取所有的汇编指令并将其存储到所述钩子函数的代码存储区,并在所述代码存储区的指令后加入一条跳转指令,所述跳转指令用于跳转到与所述指定接口函数中被读取的指令相邻的指令,并把所述代码存储区的属性设置为可执行可读;步骤S2-6 :所述壳将所述接口的代码属性修改为可写,将所述指定接口函数中被读取的指令替换为跳转到所述对应的钩子函数指令,并恢复所述接口的代码属性;步骤S2-7 :所述壳获取所述加壳程序在存储设备上的访问路径;
步骤S2-8 :所述壳根据第一偏移地址读取加密头,获取所述加密头的大小和加密原程序所使用的密钥;步骤S2-9 :所述壳使用所述密钥对加密后的原程序进行解密;步骤S2-10 :所述壳读取并存储所述原程序的关键数据;所述关键数据包括所述原程序中节的数目、原程序入口点、原程序基址、指向引入表和重定位表的数据目录、节表数据结构;步骤S2-11 :所述壳读取所述关键数据中的所述节表数据结构,获取所述原程序的各个节在所述内存中的布局,并按照所述布局将所述原程序的各个节加载到所述内存中;步骤S2-12 :所述壳读取所述关键数据中的所述引入表,获取所述原程序各个节所需的动态链接库和所需动态链接库中的函数;步骤S2-13 :所述壳将所述动态链接库加载到所述内存中,获取所述关键数据中各个节所需动态链接库中的函数在所述内存中的地址,将所述地址写入相应的节中;步骤S2-14 :所述壳比较所述原程序加载到所述内存后的程序基址与所述关键数据中的所述原程序基址是否相同,是则执行步骤S2-16,否则执行步骤S2-15 ;步骤S2-15 :所述壳找到需要被重新定位的地址,根据所述原程序加载到所述内存后的所述程序基址与所述原程序基址对需要被重新定位的地址进行重定位,顺序执行步骤 S2-16 ;步骤S2-16 :所述壳修改所述内存中节的属性。所述步骤S4还包括当第六钩子函数被调用时,所述第六钩子函数修改所述接口代码属性,将所述钩子函数代码存储区内的指定接口函数的指令写回到对应的所述接口函数中的相应位置,删除所述指定接口函数中所述跳转到钩子函数指令,并恢复所述接口代码属性,调用退出进程函数,所述第六钩子函数结束。所述步骤S4中当第五钩子函数被调用时,具体包括步骤S4-1 :所述第五钩子函数获取所述映射文件函数中的句柄参数,判断所述句柄参数是否为所述映射文件句柄,是则执行步骤S4-3,否则执行步骤S4-2 ;步骤S4-2 :所述第五钩子函数调用所述第五钩子函数的代码存储区中的映射文件函数,将与所述句柄参数对应的内容映射到内存,所述第五钩子函数结束;步骤S4-3 :所述第五钩子函数用所述映射文件偏移地址加上所述壳的长度更新映射文件的偏移地址,用映射文件大小加上加密头的长度更新映射文件大小;步骤S4-4 :所述第五钩子函数调用所述第五钩子函数的代码存储区内的所述映射文件函数,根据更新后的映射文件偏移地址和更新后的映射文件大小读取相应内容并将其映射到所述内存;步骤S4-5 :所述第五钩子函数读取所述加密头并对其进行校验,获取加密原程序的密钥;步骤S4-6 :所述第五钩子函数对映射到所述内存中的内容进行解密,将解密后的内容返回给所述原程序,所述第五钩子函数结束。所述步骤S5包括步骤S5-1 :所述第三钩子函数判断所述将要读取的内容的大小是否在所述加密后的原程序的大小范围内,是则执行步骤S5-3,否则执行步骤S5-2 ;步骤S5-2 :所述第三钩子函数调用所述第三钩子函数的代码存储区内的读文件函数,读取相应内容,对读取到的内容进行解密,并将解密后的内容返回给所述原程序,所述第三钩子函数结束;步骤S5-3 :所述第三钩子函数把待读取位置偏移加上所述壳的长度;步骤S5-4 :所述第三钩子函数调用所述代码存储区中的读文件函数读取加密头并对其进行校验,并获得加密原程序的密钥,同时获得所述加密头的大小,将待读取位置偏移加上加密头的大小;步骤S5-5 所述第三钩子函数根据待读取位置偏移读取加密后的原程序;步骤S5-6 :所述第三钩子函数对读取到的内容进行解密,并将解密后的内容返回给原程序,所述第三钩子函数结束。一种运行含有自校验的加壳程序的装置,所述装置包括获取模块,修改模块,解密模块,加载模块,转交模块,访问文件模块,获取文件大小模块,读文件模块,创建文件映射模块,映射文件模块;所述获取模块,用于获取指定接口函数的内存地址;所述修改模块,用于修改所述指定接口函数中的指令,修改对应的钩子函数的代码存储区中的指令;所述解密模块,用于获取加壳程序在存储设备上的访问路径,将加密后的原程序解密到内存中;所述加载模块,用于将所述解密后的原程序加载到所述内存中;所述转交模块,用于将控制权交给所述原程序的入口点;所述访问文件模块,用于当第一钩子函数被调用时,所述第一钩子函数调用访问文件函数,返回调用结果,判断所述访问文件函数的位置参数是否与所述加壳程序在所述存储设备上的访问路径一致,若一致则判断所述调用结果是否包含第一句柄,是则保存所述第一句柄,所述第一钩子函数结束,否则所述第一钩子函数结束;若不一致则所述第一钩子函数结束;所述获取文件大小模块,用于当第二钩子函数被调用时,第二钩子函数读取获取文件大小函数中的句柄参数,判断所述句柄参数是否为保存的所述第一句柄,是则所述第二钩子函数调用获取文件大小函数,获取第一文件大小,对所述第一文件大小进行计算得到第二文件大小,将所述第二文件大小返回给所述原程序,所述第二钩子函数结束;否则,所述第二钩子函数调用所述获取文件大小函数,返回调用结果,所述第二钩子函数结束;所述读文件模块,用于当第三钩子函数被调用时,第三钩子函数读取读文件函数中的句柄参数,判断所述句柄参数是否为保存的所述第一句柄,是则所述第三钩子函数调用读文件函数,读取相应内容,对读取到的内容进行解密,并将解密后的内容返回给所述原程序,所述第三钩子函数结束,否则,所述第三钩子函数调用所述读文件函数读取相应内容,将读取到的内容返回给所述原程序,所述第三钩子函数结束;所述创建文件映射模块,当第四钩子函数被调用时,所述第四钩子函数调用创建文件映射函数,返回调用结果,判断创建文件映射函数的句柄参数是否与保存的所述第一句柄一致,若一致则判断所述调用结果是否包含映射文件句柄,是则保存所述映射文件句柄,所述第四钩子函数结束,否则所述第四钩子函数结束;若不一致则所述第四钩子函数结束;所述映射文件模块,用于当第五钩子函数被调用时,所述第五钩子函数读取所述映射文件函数中的句柄参数,判断所述句柄参数是否为保存的所述映射文件句柄,是则所述第五钩子函数调用所述映射文件函数,根据壳的长度和加密头的大小将所述原程序中的与所述句柄参数对应的内容映射到所述内存,对所述内存中映射到的内容进行解密,将解密后的内容返回给所述原程序,所述第五钩子函数结束;否则,所述第五钩子函数将与所述句柄参数对应的内容映射到所述内存,返回所述映射的内容,所述第五钩子函数结束。所述装置包括退出进程模块;所述退出进程模块,用于当第六钩子函数被调用时,第六钩子函数修改所述接口代码属性,将所述钩子函数代码存储区内的指定接口函数的指令写回到对应的所述接口函数中的相应位置,删除所述指定接口函数中所述跳转到钩子函数指令,并恢复所述接口代码属性,调用退出进程函数,所述第六钩子函数结束。所述修改模块包括第一获取单元,解析单元,第一判断单元,更新单元,读取单元,写指令单元,属性设置单元;所述第一获取单元,用于获取所述跳转到对应的钩子函数指令的长度;所述解析单元,用于从所述指定接口函数中的第一条指令中解析出完整的汇编指令,并将所述汇编指令的长度作为当前指令长度;所述第一判断单元,用于判断所述当前指令长度是否小于所述跳转到对应的钩子函数指令的长度;所述更新单元,用于将解析出的下一条汇编指令的长度与当前指令长度的和更新当前指令长度;所述读取单元,用于读取所有的汇编指令并将其存储到对应的钩子函数的代码存储区;所述写指令单元,用于在所述钩子函数的代码存储区的指令后写入一个跳转指令,跳转到与所述指定接口函数中被读取的指令相邻的指令,还用于将所述指定接口函数中被读取的指令替换为所述跳转到对应的钩子函数的指令;所述属性设置单元,用于将所述代码存储区内的属性设置为可执行可读。所述解密模块包括第二获取单元,解密单元;所述第二获取单元,用于获取所述加壳程序在存储设备上的访问路径,根据第一偏移地址获取加密头,获取所述加密头的大小和加密原程序所使用的密钥;所述解密单元,用于使用所述第三获取单元获取的密钥将加密后的原程序解密到所述内存中。所述加载模块包括存储单元,加载单元,第三获取单元,第二判断单元,重定位单元,属性单元;所述存储单元,用于读取并存储所述原程序的关键数据;所述加载单元,用于读取节表数据结构,获取所述原程序的各个节在所述内存中的布局,并按照所述布局将所述原程序的各个节加载到所述内存中,还用于根据引入表获取所述原程序各个节所需的动态链接库和所需动态链接库中的函数;
所述第三获取单元,用于将动态链接库加载到所述内存中,获取关键数据中各个节所需动态链接库中的函数在所述内存中的地址,将所述地址写入相应的节中;所述第二判断单元,用于比较所述原程序加载到所述内存后的程序基址与原程序基址是否相同;所述重定位单元,用于找到需要被重新定位的地址,根据所述原程序加载到所述内存后的所述程序基址与存储在头部的所述原程序基址对需要被重新定位的地址进行重定位;所述属性单元,用于修改所述内存中节的属性。所述读文件模块包括第三判断单元,读文件单元和第一解密单元;所述第三判断单元,用于读取读文件函数中的句柄参数,判断句柄参数是否为所述第一句柄,还用于判断将要读取的内容的大小是否在加密后的原程序的大小范围内;所述读文件单元,用于读取相应内容,将读取到的内容返回给所述原程序;所述第一解密单元,用于对读取到的内容进行解密。所述映射文件模块包括第四判断单元,映射文件单元和第二解密单元;所述第四判断单元,用于获取所述映射文件函数中的句柄参数,判断所述句柄参数是否为所述映射文件句柄;所述映射文件单元,用于将原程序中的相应内容映射到所述内存,返回所述原程序中的相应内容;所述第二解密单元,用于对映射到的内容进行解密,返回解密后的内容。本发明与现有技术相比,具有以下优点在加壳程序运行时,使加壳程序通过自校验,保证了加壳后的程序的安全性。
图1是本发明实施例1提供的一种运行含有自校验的加壳程序的方法流程图;图2和图3是本发明实施例2提供的一种运行含有自校验的加壳程序的方法流程图;图4是本发明实施例3提供的一种运行含有自校验的加壳程序的装置方框图。
具体实施例方式下面将结合本发明实施例中的附图,对本发明实施例中的技术方案进行清楚、完整地描述,显然,所描述的实施例仅仅是本发明一部分实施例,而不是全部的实施例。基于本发明中的实施例,本领域普通技术人员在没有做出创造性劳动前提下所获得的所有其他实施例,都属于本发明保护的范围。实施例1本发明的实施例1提供一种运行含有自校验的加壳程序的方法,如图1所示,本实施例中,加壳后程序由壳、加密头部和加密后的原程序顺序组成,本方法包括步骤101 :壳获取指定接口函数的内存地址;步骤102 :壳将指定接口函数中的部分指令替换为跳转到对应的钩子函数的跳转指令,将指定接口函数中的部分指令保存到与指定接口函数对应的钩子函数的代码存储区中,再加入跳转回对应的接口函数的跳转指令,壳获取加壳程序在存储设备上的访问路径,将加密后的原程序解密到内存中;步骤103 :壳将控制权交给原程序的入口点;步骤104 :钩子函数等待被调用;当第一钩子函数被调用时,第一钩子函数调用访问文件函数,返回调用结果,判断访问文件函数的位置参数是否与加壳程序在存储设备上的访问路径一致,若一致则判断调用结果是否包含第一句柄,是则保存第一句柄,第一钩子函数结束,否则第一钩子函数结束;若不一致则第一钩子函数结束;当第二钩子函数被调用时,第二钩子函数读取获取文件大小函数中的句柄参数,判断该句柄参数是否为保存的第一句柄,是则调用第二钩子函数的代码存储区中的获取文件大小函数,获取第一文件大小,对第一文件大小进行计算得到第二文件大小,将第二文件大小返回给原程序,第二钩子函数结束;否则,第二钩子函数调用获取文件大小函数,返回调用结果,第二钩子函数结束;当第三钩子函数被调用时,第三钩子函数读取读文件函数中的句柄参数,判断该句柄参数是否为保存的第一句柄,是则执行步骤105,否则,第三钩子函数调用第三钩子函数的代码存储区中的读文件函数,读取相应内容,将读取到的内容返回给原程序,第三钩子函数结束;当第四钩子函数被调用时,第四钩子函数调用创建文件映射函数,返回调用结果,判断创建文件映射函数的句柄参数是否与保存的第一句柄一致,若一致则判断调用结果是否包含映射文件句柄,是则保存映射文件句柄,第四钩子函数结束,否则第四钩子函数结束;若不一致则第四钩子函数结束;当第五钩子函数被调用时,第五钩子函数读取映射文件函数中的句柄参数,判断该句柄参数是否为保存的映射文件句柄,是则调用第五钩子函数的代码存储区内的映射文件函数,根据壳的长度和加密头的大小将原程序中的相应内容映射到内存,对映射到的内容进行解密,将解密后的内容返回给原程序,第五钩子函数结束;否则,第五钩子函数调用第五钩子函数的代码存储区中的映射文件函数,将原程序中的相应内容映射到内存,返回映射的内容,第五钩子函数结束;本实施例中的钩子函数还包括第六钩子函数,当第六钩子函数被调用时,第六钩子函数修改接口代码属性,将第六钩子函数的代码存储区内的指定接口函数的指令写回到对应的接口函数中的相应位置,删除指定接口函数中跳转到钩子函数的指令,并恢复接口代码属性,调用第六钩子函数的代码存储区中的退出进程函数,第六钩子函数结束。步骤105 :第三钩子函数判断将要读取的内容的大小是否在加密后的原程序的大小范围内,是则调用第三钩子函数的代码存储区内的读文件函数,根据壳的长度和加密头的大小读取原程序中的相应内容,对读取到的内容进行解密,并将解密后的内容返回给原程序,第三钩子函数结束;否则,第三钩子函数调用第三钩子函数的代码存储区内的读文件函数,读取相应内容,对读取到的内容进行解密,并将解密后的内容返回给原程序,第三钩子函数结束;实施例2本发明的实施例2提供一种运行含有自校验的加壳程序的方法,如图2和图3所示,本实施例中,加壳后程序由壳、加密头部和加密后的原程序顺序组成,本方法包括步骤201 :壳获取指定的API函数的内存地址;具体地,本实施例中,步骤201包括步骤201-1 :壳加载含有需要被修改的API的系统动态库kernel32. dll ;具体地,本步骤通过调用系统接口 LoadLibray完成;步骤201-2 :壳从系统动态库获取需要被修改的API函数的访问地址;具体地,本步骤通过调用系统接口 GetProcAddress完成,根据预定的接口名称检索访问地址;预定接口包括用于访问文件的函数CreateFile,用于获得文件在硬盘上的大小的函数GetFileSize,用于读文件的函数ReadFile,用于创建文件映射的函数CreateFileMapping,用于把映射文件映射到内存的函数MapViewOfFile ;进一步地,预定接口还包括用于退出进程的函数ExitProcess ;例如,以CreateFile函数为例,CreateFile函数对应的内存地址为0x7C810800,如下7C810800 mov edi,edi7C810802 push ebp7C810803 mov ebp, esp7C810805 sub esp, 587C810808 mov eax, dword ptr [ebp+18]7C81080B dec eax......步骤202 :壳判断内存地址是否获取成功,是则执行步骤203,否则壳返回Hook函数初始化失败的信息,结束;步骤203 :壳获取跳转到Hook函数指令的长度;例如,本实施例中,CreateFile函数跳转到第一 Hook函数的汇编指令为jmptest. 00639f20,指令长度为5个字节;步骤204 :壳将API中指定接口函数的第一条指令解析出完整的汇编指令,并将该汇编指令长度作为当前指令长度;步骤205 :壳判断当前指令长度是否小于跳转到Hook函数的汇编指令的长度,是则执行步骤206,否则执行步骤207 ;步骤206 :壳将API中指定接口函数的下一条指令解析出完整的汇编指令,用该汇编指令长度与当前指令长度的和更新当前指令长度,返回步骤205 ;例如,本实施例中,壳共解析出CreateFile函数中的三条指令,如7C810800 mov edi, edi7C810802 push ebp7C810803 mov ebp, esp步骤207 :壳读取解析出的所有汇编指令到Hook函数的代码存储区,并在此代码存储区的汇编指令后加入一条跳转指令,跳转到API中的指定函数的被读取指令之后相邻的指令,并把此代码存储区内的属性置为可执行可读;例如,本实施例中,将OeateFile函数的三条指令存储在地址为0x001638EA的内存上,如001638EA mov edi, edi001638EC push ebp001638ED mov ebp, esp在三条指令后加入一个跳转指令,跳转到API中CreateFile函数的内存地址为0x7C810805的指令,如001638EA mov edi, edi001638EC push ebp001638ED mov ebp, esp001638EF jmp dword ptr[1638F5]001638F5 add eax, 7C8108步骤208 :壳将API代码属性修改为可写,将API中指定函数的被读取的指令替换为跳转到Hook函数指令,并恢复原API代码属性;例如7C810800 jmp text.00639F207C810805 sub esp, 587C810808 mov eax, dword ptr[ebp+18]7C81080B dec eax......步骤209 :壳获取加壳程序在存储设备上的访问路径;具体地,本实施例中,壳通过调用GetModuleFiIeName函数获得加壳程序在存储设备上的位置;步骤210 :壳根据第一偏移地址读取加密头,获取加密头的大小和加密原程序所使用的密钥;本实施例中,第一偏移地址为加密头在加壳程序中的文件偏移地址;具体地,本实施例中,第一偏移地址为0x1000 ;具体地,本实施例中,加密头的大小为0x100,即地址0x1000到0x1100的内容;步骤211 :壳使用密钥将加密后的原程序解密到内存中;步骤212:壳检查内存中原程序的头部是否正确,是则执行步骤213,否则返回错
误信息;具体地,本实施例中,步骤212包括步骤212-1 :壳读取内存中原程序头部中的DOS头,检查DOS头的前两个字节是否是MZ标识,是则执行步骤212-2,否则返回错误信息;例如,本实施例中,DOS头如下4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00BB 00 00 00 00 00 00 00 40 00 00 00 00 00 00 000000 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 EO 00 00 00其中,MZ标识为4D 5A ;
步骤212-2:壳读取DOS头的尾部中指向PE头的文件偏移地址,根据该文件偏移地址获得PE头;步骤212-3:壳读取内存中原程序头部中的PE头,检查PE头的前两个字节是否是PE标识,是则执行步骤213,否则返回错误信息;例如,本实施例中,PE头如下:50 45 00 00 4C 01 05 00 CA B7 39 4F 00 00 00 0000 00 00 00 EO 00 02 01其中,PE标识为:50 45 ;步骤213:壳读取并存储原程序头部中的关键数据;本实施例中,关键数据存储在原程序头部的PE头中;本实施例中,关键数据包括:原程序中节的数目、原程序入口点、原程序基址、用于指向引入表和重定位表的数据目录,例如:0007DD37 EntryPoint(rva)0007D137 EntryPoint(raw)00400000 ImageBase004C2000 Size of Image00001000 Sections Alignment00000200 File Alignment0005Number of sections步骤214:壳读取关键数据中的节表数据结构,获取原程序的各个节在内存中的布局,并按照该布局将原程序的各个节加载到内存中;例如,本实施例中,代码节从地址0x501000开始加载,共加载0x1000个字节;步骤215:壳根据数据目录读取关键数据中的引入表,获取原程序各个节所需的动态链接库和所需动态链接库中的函数;例如,需加载到内存中的动态链接库中的函数包括:WS2_32.dl1、MPR.dl1、C0MCTL32.dll、VERSION.dll、credu1.dll、SETUPAP1.dll、KERNEL.dll、USER32.dll、ffl)I32.dll、C0MDLG32.dll ;步骤216:壳将动态链接库加载到内存中,获取关键数据中各个节所需动态链接库中的函数在内存中的地址,将地址写入相应的节中;步骤217:壳比较原程序加载到内存后的程序基址与关键数据中的的原程序基址是否相同,是则执行步骤219,否则执行步骤218;具体地,本实施例中,存储在头部的原程序基址为0x400000,原程序加载到内存后的程序基址为0x500000 ;
步骤218:壳根据重定位表找到需要被重新定位的地址,根据原程序加载到内存后的程序基址与存储在头部的原程序基址对需要被重新定位的地址进行重定位,执行步骤219 ;本实施例中,重定位表指向关于重定位地址的结构,根据此结构找到需要被重新定位的地址;具体地,在本实施例中,两个基址相差0x100000,则将需要被重新定位的地址加上0x100000,作为重定位后的地址;步骤219:壳根据节表数据结构中的节属性,修改内存中节的属性;例如,每个节的节属性如下:
权利要求
1.一种运行含有自校验的加壳程序的方法,其特征在于,所述方法包括: 步骤S1:壳获取指定接口函数的内存地址; 步骤S2:所述壳将所述指定接口函数中的部分指令替换为跳转到对应的钩子函数的跳转指令,将所述指定接口函数的所述部分指令保存到与所述指定接口函数对应的钩子函数的代码存储区中,再加入跳转回对应的所述接口函数的跳转指令,所述壳获取加壳程序在存储设备上的访问路径,将加密后的原程序解密到内存中; 步骤S3:所述壳将控制权交给所述原程序的入口点; 步骤S4:所述钩子函数等待被调用; 当第一钩子函数被调用时,所述第一钩子函数调用访问文件函数,返回调用结果,判断所述访问文件函数的位置参数是否与所述加壳程序在所述存储设备上的访问路径一致,若一致则判断所述调用结果是否包含第一句柄,是则保存所述第一句柄,所述第一钩子函数结束,否则所述第一钩子函数结束;若不一致则所述第一钩子函数结束; 当第二钩子函数被调用时,所述第二钩子函数读取获取文件大小函数中的句柄参数,判断所述句柄参数是否为保存的所述第一句柄,是则所述第二钩子函数调用获取文件大小函数,获取第一文件大小,对所述第一文件大小进行计算得到第二文件大小,将所述第二文件大小返回给所述原程序,所述第二钩子函数结束;否则,所述第二钩子函数调用所述获取文件大小函数,返回调用结果,所述第二钩子函数结束; 当第三钩子函数被调用时,所述第三钩子函数读取读文件函数中的句柄参数,判断所述句柄参数是否为保存的所述第一句柄,是则执行步骤S5,否则,所述第三钩子函数调用所述读文件函数读取相应内容,将读取到的内容返回给所述原程序,所述第三钩子函数结束; 当第四钩子函数被调用时,所述第四钩子函数调用创建文件映射函数,返回调用结果,判断所述创建文件映射函数的句柄参数是否与保存的所述第一句柄一致,若一致则判断所述调用结果是否包含映射文件句柄,是则保存所述映射文件句柄,所述第四钩子函数结束,否则所述第四钩子函数结束;若不一致则所述第四钩子函数结束; 当第五钩子函数被调用时,所述第五钩子函数读取所述映射文件函数中的句柄参数,判断所述句柄参数是否为保存的所述映射文件句柄,是则所述第五钩子函数调用所述映射文件函数,根据所述壳的长度和加密头的大小将所述原程序中的与所述句柄参数对应的内容映射到所述内存中,对所述内存中的映射到的内容进行解密,将解密后的内容返回给所述原程序,所述第五钩子函数结束;否则,所述第五钩子函数将与所述句柄参数对应的内容映射到所述内存,返回所述映射的内容,所述第五钩子函数结束; 步骤S5:所述第三钩子函数判断将要读取的内容的大小是否在所述加密后的原程序的大小范围内,是则所述第三钩子函数调用所述读文件函数,根据所述壳的长度和加密头的大小读取所述原程序中的相应内容,对读取到的内容进行解密,并将解密后的内容返回给所述原程序,所述第三钩子函数结束;否则,所述第三钩子函数调用所述读文件函数,读取相应内容,对所述读取到的内容进行解密,并将解密后的内容返回给所述原程序,所述第三钩子函数结束。
2.根据权利要求1所述的方法,其特征在于,所述步骤SI中所述指定接口函数为所述访问文件函数或所述获取文件大小函数或所述读文件函数或所述创建文件映射函数或所述映射文件函数。
3.根据权利要求2所述的方法,其特征在于,所述步骤S2包括: 步骤S2-1:所述壳获取所述跳转到对应的钩子函数的指令的长度; 步骤S2-2:所述壳从所述指定接口函数中的第一条指令中解析出完整的汇编指令,并将所述汇编指令的长度作为当前指令长度; 步骤S2-3:所述壳判断当前指令长度是否小于所述跳转到对应的钩子函数的指令的长度,是则执行步骤S2-4,否则执行步骤S2-5 ; 步骤S2-4:所述壳将所述指定接口函数中的下一条指令解析出完整的汇编指令,用所述汇编指令的长度与当前指令长度的和更新当前指令长度,执行步骤S2-3 ; 步骤S2-5:所述壳读取所有的汇编指令并将其存储到所述钩子函数的代码存储区,并在所述代码存储区的指令后加入一条跳转指令,所述跳转指令用于跳转到与所述指定接口函数中被读取的指令相邻的指令,并把所述代码存储区的属性设置为可执行可读; 步骤S2-6:所述壳将所述接口的代码属性修改为可写,将所述指定接口函数中被读取的指令替换为跳转到所述对应的钩子函数指令,并恢复所述接口的代码属性; 步骤S2-7:所述壳获取所述加壳程序在存储设备上的访问路径; 步骤S2-8:所述壳根据第一偏移地址读取加密头,获取所述加密头的大小和加密原程序所使用的密钥; 步骤S2-9:所述壳使用所述密钥对加密后的原程序进行解密; 步骤S2-10:所述壳读取并存储所述原程序的关键数据;所述关键数据包括所述原程序中节的数目、原程序入口点、原程序基址、指向引入表和重定位表的数据目录、节表数据结构; 步骤S2-11:所述壳读取所述关键数据中的所述节表数据结构,获取所述原程序的各个节在所述内存中的布局,并按照所述布局将所述原程序的各个节加载到所述内存中;步骤S2-12:所述壳读取所述关键数据中的所述引入表,获取所述原程序各个节所需的动态链接库和所需动态链接库中的函数; 步骤S2-13:所述壳将所述动态链接库加载到所述内存中,获取所述关键数据中各个节所需动态链接库中的函数在所述内存中的地址,将所述地址写入相应的节中; 步骤S2-14:所述壳比较所述原程序加载到所述内存后的程序基址与所述关键数据中的所述原程序基址是否相同,是则执行步骤S2-16,否则执行步骤S2-15 ; 步骤S2-15:所述壳找到需要被重新定位的地址,根据所述原程序加载到所述内存后的所述程序基址与所述原程序基址对需要被重新定位的地址进行重定位,顺序执行步骤S2-16 ; 步骤S2-16:所述壳修改所述内存中节的属性。
4.根据权利要求3所述的方法,其特征在于,所述步骤S4还包括:当第六钩子函数被调用时,所述第六钩子函数修改所述接口代码属性,将所述钩子函数代码存储区内的指定接口函数的指令写回到对应的所述接口函数中的相应位置,删除所述指定接口函数中所述跳转到钩子函数指令,并恢复所述接口代码属性,调用退出进程函数,所述第六钩子函数结束。
5.根据权利要求1所述的方法,其特征在于,所述步骤S4中当第五钩子函数被调用时,具体包括: 步骤S4-1:所述第五钩子函数获取所述映射文件函数中的句柄参数,判断所述句柄参数是否为所述映射文件句柄,是则执行步骤S4-3,否则执行步骤S4-2 ; 步骤S4-2:所述第五钩子函数调用所述第五钩子函数的代码存储区中的映射文件函数,将与所述句柄参数对应的内容映射到内存,所述第五钩子函数结束; 步骤S4-3:所述第五钩子函数用所述映射文件偏移地址加上所述壳的长度更新映射文件的偏移地址,用映射文件大小加上加密头的长度更新映射文件大小; 步骤S4-4:所述第五钩子函数调用所述第五钩子函数的代码存储区内的所述映射文件函数,根据更新后的映射文件偏移地址和更新后的映射文件大小读取相应内容并将其映射到所述内存; 步骤S4-5:所述第五钩子函数读取所述加密头并对其进行校验,获取加密原程序的密钥; 步骤S4-6:所述第五钩子函数对映射到所述内存中的内容进行解密,将解密后的内容返回给所述原程序,所述第五钩子函数结束。
6.根据权利要求1所述的方法,其特征在于,所述步骤S5包括: 步骤S5-1:所述第三钩子函数判断所述将要读取的内容的大小是否在所述加密后的原程序的大小范围内,是则执行步骤S5-3,否则执行步骤S5-2 ; 步骤S5-2:所述第三钩子函数调用所述第三钩子函数的代码存储区内的读文件函数,读取相应内容, 对读取到的内容进行解密,并将解密后的内容返回给所述原程序,所述第三钩子函数结束; 步骤S5-3:所述第三钩子函数把待读取位置偏移加上所述壳的长度; 步骤S5-4:所述第三钩子函数调用所述代码存储区中的读文件函数读取加密头并对其进行校验,并获得加密原程序的密钥,同时获得所述加密头的大小,将待读取位置偏移加上加密头的大小; 步骤S5-5:所述第三钩子函数根据待读取位置偏移读取加密后的原程序; 步骤S5-6:所述第三钩子函数对读取到的内容进行解密,并将解密后的内容返回给原程序,所述第三钩子函数结束。
7.一种运行含有自校验的加壳程序的装置,其特征在于,所述装置包括获取模块,修改模块,解密模块,加载模块,转交模块,访问文件模块,获取文件大小模块,读文件模块,创建文件映射模块,映射文件模块; 所述获取模块,用于获取指定接口函数的内存地址; 所述修改模块,用于修改所述指定接口函数中的指令,修改对应的钩子函数的代码存储区中的指令; 所述解密模块,用于获取加壳程序在存储设备上的访问路径,将加密后的原程序解密到内存中; 所述加载模块,用于将所述解密后的原程序加载到所述内存中; 所述转交模块,用于将控制权交给所述原程序的入口点; 所述访问文件模块,用于当第一钩子函数被调用时,所述第一钩子函数调用访问文件函数,返回调用结果,判断所述访问文件函数的位置参数是否与所述加壳程序在所述存储设备上的访问路径一致,若一致则判断所述调用结果是否包含第一句柄,是则保存所述第一句柄,所述第一钩子函数结束,否则所述第一钩子函数结束;若不一致则所述第一钩子函数结束; 所述获取文件大小模块,用于当第二钩子函数被调用时,第二钩子函数读取获取文件大小函数中的句柄参数,判断所述句柄参数是否为保存的所述第一句柄,是则所述第二钩子函数调用获取文件大小函数,获取第一文件大小,对所述第一文件大小进行计算得到第二文件大小,将所述第二文件大小返回给所述原程序,所述第二钩子函数结束;否则,所述第二钩子函数调用所述获取文件大小函数,返回调用结果,所述第二钩子函数结束; 所述读文件模块,用于当第三钩子函数被调用时,第三钩子函数读取读文件函数中的句柄参数,判断所述句柄参数是否为保存的所述第一句柄,是则所述第三钩子函数调用读文件函数,读取相应内容,对读取到的内容进行解密,并将解密后的内容返回给所述原程序,所述第三钩子函数结束,否则,所述第三钩子函数调用所述读文件函数读取相应内容,将读取到的内容返回给所述原程序,所述 第三钩子函数结束; 所述创建文件映射模块,用于当第四钩子函数被调用时,所述第四钩子函数调用创建文件映射函数,返回调用结果,判断创建文件映射函数的句柄参数是否与保存的所述第一句柄一致,若一致则判断所述调用结果是否包含映射文件句柄,是则保存所述映射文件句柄,所述第四钩子函数结束,否则所述第四钩子函数结束;若不一致则所述第四钩子函数结束; 所述映射文件模块,用于当第五钩子函数被调用时,所述第五钩子函数读取所述映射文件函数中的句柄参数,判断所述句柄参数是否为保存的所述映射文件句柄,是则所述第五钩子函数调用所述映射文件函数,根据壳的长度和加密头的大小将所述原程序中的与所述句柄参数对应的内容映射到所述内存,对所述内存中映射到的内容进行解密,将解密后的内容返回给所述原程序,所述第五钩子函数结束;否则,所述第五钩子函数将与所述句柄参数对应的内容映射到所述内存,返回所述映射的内容,所述第五钩子函数结束。
8.根据权利要求7所述的方法,其特征在于,所述装置包括退出进程模块; 所述退出进程模块,用于当第六钩子函数被调用时,第六钩子函数修改所述接口代码属性,将所述钩子函数代码存储区内的指定接口函数的指令写回到对应的所述接口函数中的相应位置,删除所述指定接口函数中所述跳转到钩子函数指令,并恢复所述接口代码属性,调用退出进程函数,所述第六钩子函数结束。
9.根据权利要求7所述的方法,其特征在于,所述修改模块包括第一获取单元,解析单元,第一判断单元,更新单元,读取单元,写指令单元,属性设置单元; 所述第一获取单元,用于获取所述跳转到对应的钩子函数指令的长度; 所述解析单元,用于从所述指定接口函数中的第一条指令中解析出完整的汇编指令,并将所述汇编指令的长度作为当前指令长度; 所述第一判断单元,用于判断所述当前指令长度是否小于所述跳转到对应的钩子函数指令的长度; 所述更新单元,用于将解析出的下一条汇编指令的长度与当前指令长度的和更新当前指令长度; 所述读取单元,用于读取所有的汇编指令并将其存储到对应的钩子函数的代码存储区; 所述写指令单元,用于在所述钩子函数的代码存储区的指令后写入一个跳转指令,跳转到与所述指定接口函数中被读取的指令相邻的指令,还用于将所述指定接口函数中被读取的指令替换为所述跳转到对应的钩子函数的指令; 所述属性设置单元,用于将所述代码存储区内的属性设置为可执行可读。
10.根据权利要求7所述的方法,其特征在于,所述解密模块包括第二获取单元,解密单元; 所述第二获取单元,用于获取所述加壳程序在存储设备上的访问路径,根据第一偏移地址获取加密头,获取所述加密头的大小和加密原程序所使用的密钥; 所述解密单元,用于使用所述第三获取单元获取的密钥将加密后的原程序解密到所述内存中。
11.根据权利要求7所述的方法,其特征在于,所述加载模块包括存储单元,加载单元,第三获取单元,第二判断单元,重定位单元,属性单元; 所述存储单元,用于读取并存储所述原程序的关键数据; 所述加载单元,用于读取节表数据结构,获取所述原程序的各个节在所述内存中的布局,并按照所述布局将所述原程序的各个节加载到所述内存中,还用于根据引入表获取所述原程序各个节所需的动态链接库和所需动态链接库中的函数; 所述第三获取单元,用于将动态链接库加载到所述内存中,获取关键数据中各个节所需动态链接库中的函数在所述内存中的地址,将所述地址写入相应的节中; 所述第二判断单元,用于 比较所述原程序加载到所述内存后的程序基址与原程序基址是否相同; 所述重定位单元,用于找到需要被重新定位的地址,根据所述原程序加载到所述内存后的所述程序基址与存储在头部的所述原程序基址对需要被重新定位的地址进行重定位; 所述属性单元,用于修改所述内存中节的属性。
12.根据权利要求7所述的方法,其特征在于,所述读文件模块包括第三判断单元,读文件单元和第一解密单元; 所述第三判断单元,用于读取读文件函数中的句柄参数,判断句柄参数是否为所述第一句柄,还用于判断将要读取的内容的大小是否在加密后的原程序的大小范围内; 所述读文件单元,用于读取相应内容,将读取到的内容返回给所述原程序; 所述第一解密单元,用于对读取到的内容进行解密。
13.根据权利要求7所述的方法,其特征在于,所述映射文件模块包括第四判断单元,映射文件单元和第二解密单元; 所述第四判断单元,用于获取所述映射文件函数中的句柄参数,判断所述句柄参数是否为所述映射文件句柄; 所述映射文件单元,用于将原程序中的相应内容映射到所述内存,返回所述原程序中的相应内容; 所述第二解密单元,用于对映射到的内容进行解密,返回解密后的内容。
全文摘要
本发明公开一种运行含有自校验的加壳程序的方法和装置,该方法包括壳将原程序加载到内存中,钩子函数等待被调用,当钩子函数被调用时,执行相应操作,将结果返回给原程序。本发明公开的一种运行含有自校验的加壳程序的方法和装置,在加壳程序运行时,使加壳程序通过自校验,保证了加壳后的程序的安全性。
文档编号G06F11/10GK103077332SQ201210587208
公开日2013年5月1日 申请日期2012年12月28日 优先权日2012年12月28日
发明者陆舟, 于华章 申请人:飞天诚信科技股份有限公司