一种基于编译中间结果的软件保护方法
【专利摘要】本发明公开了一种针对编译中间结果的软件保护方法,包括以下步骤:S1:将软件源代码进行编译得到原始的目标OBJ文件;S2:分析原始OBJ文件,得到针对原始OBJ文件的分拆数据;S3:对S2的分析结果进行预处理;S4:进行系统函数调用的隐藏和冗余函数插入,以及对系统使用的全局字符串进行的加密;S5:进行针对普通指令的变形;S6:将上述过程处理后得到的分散数据进行重组成OBJ文件,然后通过链接器链接成可执行文件。本发明实现了针对OBJ文件的指令级的混淆和保护,同时能够避免错误的产生,提高混淆效果和混淆质量,防止软件遭到非法破解以及分析。
【专利说明】一种基于编译中间结果的软件保护方法
【技术领域】
[0001] 本发明属于软件保护领域,具体涉及一种基于编译中间结果的软件保护方法。
【背景技术】
[0002] 应用程序的开发包括:源代码的编写,编译器的编译的OBJ文件,以及链接器对 OBJ文件的链接这一系列组成。每一个源文件对应一个OBJ文件,OBJ文件是由系统编译 器对高级语言进行汇编、符号处理、字符串处理之后的产物,OBJ文件符合相应平台的文档 标准,如微软C0FF文件标准等。0BJ文件相较于链接后的可执行文件来说,具有更加简单 的结构,以及更加灵活的数据索引以及代码调用方式,因此处理起来更加灵活,可以进行多 种方式的混淆,包括混淆函数导入导出表,字符串引用,增加冗余性等等。这种操作并不影 响程序原有的功能。目前,针对软件保护的措施中,有针对源代码级的软件混淆,代码混淆 (Obfuscated code)亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难 于阅读和理解的形式的行为。其中一种方式是将代码中的各种元素,如变量,函数,类的名 字改写成无意义的名字。这样带来了一系列的问题,如被混淆的代码难于理解,因此调试出 错也变得困难起来。还有一些针对可执行文件的代码保护,主要表现为对软件加壳。加壳技 术是在二进制的程序中植入一段代码,在运行的时候优先取得程序的控制权,做一些额外 的工作,这一部分的工作可以用来对原来的指令进行预处理,将原有的程序执行流程打乱, 以做到防止软件被分析和破解。以上两种方式各有优劣,源代码级别的保护很局限,对于较 为底层的语言的处理能力有限,而基于可执行文件的加壳种类不多,容易被脱壳,进而将原 有程序暴露给分析者。
【发明内容】
[0003] 本发明所要解决的技术问题是提供一种针对编译结果的软件保护方法,实现针对 0BJ文件的指令级的混淆和保护,同时能够避免错误的产生,以及提高混淆效果和混淆质 量,防止软件遭到非法破解以及分析。
[0004] 本发明的技术方案为:一种针对编译中间结果的软件保护方法,包括以下步骤: 51 :将软件源代码进行编译得到原始的目标0BJ文件; 52 :分析原始0BJ文件,得到针对原始0BJ文件的分拆数据; 53 :对S2的分析结果进行预处理; 54 :进行系统函数调用的隐藏和冗余函数插入,以及对系统使用的全局字符串进行的 加密; 55 :进行针对普通指令的变形; 56 :将上述过程处理后得到的分散数据进行重组成0BJ文件,然后通过链接器链接成 可执行文件。
[0005] S2步骤中分拆数据的结构依据微软编译器或者GCC编译器的0BJ规范文档C0FF 标准来进行,并结合混淆功能需要进行设计,分拆数据存放在内存中,分拆数据与S1步骤 中OBJ文件一一对应,且能够按照拆分的逆过程进行重组,生成原始OBJ文件。
[0006] S3步骤的预处理主要进行以下方面分析: 531 :该分拆数据是否满足进行混淆的基本条件,对不达标的执行跳过混淆步骤; 532 :针对S31得到的结果,搜集混淆操作需要的数据集合,存放到固定的内存位置,提 供给混淆模块访问; 533 :针对S32得到的分拆数据结果,根据混淆功能需要,调整分拆数据内容,使得其既 不影响后期S6步骤重组OBJ文件功能,又辅助完成S4和S5的功能。
[0007] S3具体所做的工作包括: 1> 根据模块工作内容的可行性,对其中的各种信息进行筛选,进行一些行为的预处 理,包括筛选OBJ中包含的函数节、数据节、重定位节,根据混淆的类型,筛选出OBJ文件中 能够进行的混淆,不能够进行的混淆,并保存下来; 2> 对函数节中包含的指令进行分析和处理,分析处理相对转移指令,对这部分指令 进行预处理,同时还记录和处理0BJ中包含的编译器对一些高级语言中的特殊语句进行的 优化,跳过或者用特殊的处理方式处理这部分数据; 3> 分析高级语言源代码中包含的全局变量定义位置和大小,以及相关联的数据,对 这部分数据进行存储,为后续的混淆做铺垫。
[0008] S4步骤具体为: 541 :根据S33提供的分拆数据,提取其中系统函数调用; 542 :根据配置对提取的调用进行筛选,对能够进行隐藏的系统调用则进行下一步操 作; 543 :将S42得到的系统调用的命令进行解析,提取其中API名称和模块名称; 544 :根据两部分数据,在相应的地方将原始系统调用指令替换成动态调用指令; 545 :利用随机函数,在指令序列中随机添加冗余的系统调用; 545 :根据S33提供的分拆数据,找到其中代码引用的字符串存储位置以及引用位置, 并且在S33得到的分拆数据中插入解密函数节; 546 :对字符串加密,相应的在引用位置插入解密函数调用。
[0009] S4步骤中系统调用混淆包含两个方面: S411 :针对传统系统调用的隐藏,将对系统API的直接静态调用变换为动态调用; S422 :人为添加冗余的函数调用,使得在最后生成的PE文件的导入表中能够看到这些 冗余的系统API,并且这些API随机的分布在很多函数中; S4步骤中字符串加密功能,主要针对源代码中的全局字符串进行加密,全局字符串主 要包括已经初始化的字符串。
[0010] S5步骤中进行针对普通指令的变形的具体步骤如下: 551 :将S33得到的分拆数据作为输入,对其中每一条指令进行判断,并且作为下一步 的输入; 552 :如果S51输出结果是常规指令,则根据配置对该常规指令进行替换,替换依据为 一个包含常规指令和替换指令的模板,替换类型选择根据随机算法生成; 553 :如果S51输出结果是控制转移指令,根据能否处理,进行下一步操作:如果能够处 理,则根据控制转移指令的类型,对S33得到的分拆数据进行修改,用能完成相同功能的指 令替换原来的指令;如果不能处理,则直接跳过执行。
[0011] S6步骤具体为:按照C0FF文件标准以及拆分时的步骤进行重组,重组成OBJ文 件,交给同一平台的编译器配套使用的链接器,根据原始的链接参数进行链接,链接成可执 行文件。
[0012] S1步骤中采用微软编译器或者GCC编译器进行针对源代码的编译到原始的目标 OBJ文件。
[0013] 本发明与现有技术相比具有如下优点: 本发明实现了针对OBJ文件的指令级的混淆和保护,同时能够避免错误的产生,提高 混淆效果和混淆质量,防止软件遭到非法破解以及分析。
【专利附图】
【附图说明】
[0014] 图1为本发明方法的流程图。
【具体实施方式】
[0015] 根据图1的流程图,以下进行针对本发明工作流程的具体描述,但是不作为本发 明的限定。
[0016] S1 :使用微软提供的或者GCC等源代码编译器将软件源代码编译成中间结果,得 到原始的目标OBJ文件。原始的OBJ文件能够经过同一平台链接器的链接生成原始的PE 文件。
[0017] S2 :分析原始0BJ文件,得到针对原始0BJ文件的分拆数据。分拆数据的结构依据 微软编译器或者GCC编译器的0BJ规范文档,并结合混淆功能需要进行设计,分拆数据存放 在内存中。分拆数据与上一步骤的0BJ文件一一对应,且能够按照拆分的逆过程进行重组, 生成原始0BJ文件。
[0018] S3 :对S2的分析结果进行预处理,为后续代码混淆做准备工作,确保混淆处理不 会出现无法处理的异常。进行预处理,对S2得到的分拆数据进行预处理,获取后期混淆操 作的更多确切信息,并且能够减少无法预知错误的发生。
[0019] 作为该步骤的预处理分析功能,主要进行以下几方面分析: S31 :该分拆数据是否满足进行混淆的基本条件,对不达标的执行跳过混淆步骤。
[0020] S32:针对S31得到的结果,搜集混淆操作需要的数据集合,存放到固定的内存位 置,提供给混淆模块访问,这部分数据是后期S5步骤必需的要素。
[0021] S33 :针对S32得到的分拆数据结果,根据混淆功能需要,调整分拆数据内容,使得 其既不影响后期S6步骤重组0BJ文件功能,又辅助完成S4和S5的功能。这部分数据为后 期S4和S5需要的第二阶段的分拆数据。
[0022] S4 :进行第一步的混淆,利用S33提供的第二阶段的分拆数据,进行系统函数调用 的隐藏和冗余函数插入,以及对系统使用的全局字符串进行的加密。
[0023] 具体步骤为: S41 :根据S33提供的分拆数据,提取其中系统函数调用。
[0024] S42:根据配置对提取的调用进行筛选,对能够进行隐藏的系统调用则进行下一步 操作。
[0025] S43 :将S42得到的系统调用的命令进行解析,提取其中API名称和模块名称。
[0026] S44:根据两部分数据,在相应的地方将原始系统调用指令替换成动态调用指令。
[0027] S45 :利用随机函数,在指令序列中随机添加冗余的系统调用。
[0028] S45 :根据S33提供的分拆数据,找到其中代码引用的字符串存储位置以及引用位 置。并且在S33得到的分拆数据中插入解密函数节。
[0029] S46 :对字符串加密,相应的在引用位置插入解密函数调用。
[0030] S5 :进行针对S4过程处理结果的进一步混淆,主要进行针对普通指令的变形,这 部分涉及到常规指令以及控制转移指令。
[0031] 具体步骤如下: S51 :将S33得到的分拆数据作为输入,对其中每一条指令进行判断,并且作为下一步 的输入。
[0032] S52 :如果S51输出结果是常规指令,则根据配置对该常规指令进行替换,替换依 据可以是一个包含常规指令和替换指令的模板,替换类型选择根据随机算法生成。
[0033] S53 :如果S51输出结果是控制转移指令,根据能否处理,进行下一步操作:如果能 够处理,则根据控制转移指令的类型,对S33得到的分拆数据进行修改,用能完成相同功能 的指令替换原来的指令;如果不能处理,则直接跳过执行。
[0034] S6 :将上述过程处理后得到的分散数据进行重组,按照C0FF文件标准以及拆分时 的步骤进行重组,重组成OBJ文件,交给同一平台的编译器配套使用的链接器,根据原始的 链接参数进行链接,链接成可执行文件。
[0035] S6步骤的关键步骤是利用由S3步骤提供,经过S4和S5步骤处理后的分拆数据, 解析分拆数据中包含的重组OBJ需要的数据和辅助数据,根据辅助数据修改相应的分拆数 据中包含的符号表和指令。
[0036] 实施例1 本发明是针对编译中间结果OBJ文件的软件混淆保护方法,包含以下步骤: S1步骤可以直接利用微软编译器或者GCC编译器进行针对源代码的编译。
[0037] S2步骤主要包括一些基础的分析和处理工作,并且对OBJ文件按照一定的数据结 构进行拆分。后期的处理都是针对这部分分拆的数据来进行的,因此,本步骤是主功能的第 一步实现。
[0038] 微软或者GCC提供的编译器的处理过程类似,对于中间文件的产生都是按照C0FF 标准来进行,之间存在极少的差异,这些差异可以通过一些兼容模块进行处理,来保证后期 的分析和混淆能够正常执行。本方法只适用于C/C++语言,可以通过查找微软提供的官方 文档和Windows SDK提供的一些头文件。目标文件的拆分和处理只是本发明的初始化步骤, 这里就不再多余描述,具体方法可以参考以上文档。
[0039] S3步骤作为对0BJ文件处理的关键部分,S3主要做了一些能够大幅提高稳定性的 工作,具体所做的工作包括: 1>根据模块工作内容的可行性,对其中的各种信息进行筛选,进行一些行为的预处理, 包括筛选0BJ中包含的函数节、数据节、重定位节,根据混淆的类型,筛选出0BJ文件中能够 进行的混淆,不能够进行的混淆,并保存下来。
[0040] 2>对函数节中包含的指令进行分析和处理,分析处理相对转移指令,对这部分指 令进行预处理。同时还记录和处理OBJ中包含的编译器对一些高级语言中的特殊语句进行 的优化,跳过或者用特殊的处理方式处理这部分数据。
[0041] 3>分析高级语言源代码中包含的全局变量定义位置和大小,以及相关联的数据, 对这部分数据进行存储,为后续的混淆做铺垫。
[0042] S4步骤:作为对混淆的第一步实施方案,S4步骤中主要包含系统调用混淆和字符 串加密两部分。
[0043] 作为同一目的的两个方面,系统调用混淆包含两个方面: S41 :针对传统系统调用的隐藏,将对系统API的直接静态调用变换为动态调用。动态 调用的调用过程只会留下API名称和动态库的名称,不会在最后生成的PE文件的导入表中 留下痕迹。
[0044] S42 :人为添加冗余的函数调用,使得在最后生成的PE文件的导入表中能够看到 这些冗余的系统API,并且这些API随机的分布在很多函数中。
[0045] 字符串加密功能,主要针对源代码中的全局字符串进行加密,全局字符串主要包 括已经初始化的字符串,例如控制台输出的字符串、函数调用时的实参、动态调用的API名 称和动态库名称等等。
[0046] S5步骤:对大量普遍存在的指令进行变形,这里的变形指令包括常规指令和控制 转移指令,针对两者分别进行不同的变形模式如下: S51 :对常规指令的变形,对每一条常规指令设计多套变形模板,在不影响其他寄存器 的前提下,对常规指令进行对应的替换。
[0047] S52 :对控制转移指令的变形,分析控制转移类型,针对能够处理的指令类型进行 变形。如下是几类变形方案:1、将CALL指令变换成RTN指令,2、将CALL指令变换成JMP指 令,3、将JMP绝对跳转变成成RTN指令。变形中需要对这几类指令进行分析,搜集其中包含 的符号表以及重定位信息,综合分析之后才能判断出能否进行变形。
[0048] S6步骤,对以上处理过的分拆数据进行重组,属于S2步骤的逆过程,重组过程主 要的文档参照依据是微软C0FF文件格式或者相应的编译器文档。
【权利要求】
1. 一种针对编译中间结果的软件保护方法,其特征在于,包括以下步骤: 51 :将软件源代码进行编译得到原始目标OBJ文件; 52 :分析原始OBJ文件,得到针对原始目标OBJ文件的分拆数据; 53 :对S2的分析结果进行预处理; 54 :进行系统函数调用的隐藏和冗余函数插入,以及对系统使用的全局字符串进行的 加密; 55 :进行针对普通指令的变形; 56 :将上述过程处理后得到的分散数据进行重组成OBJ文件,然后通过链接器链接成 可执行文件。
2. 根据权利要求1所述的一种针对编译中间结果的软件保护方法,其特征在于,S2步 骤中分拆数据的结构依据微软编译器或者GCC编译器的OBJ规范文档C0FF标准来进行,并 结合混淆功能需要进行设计,分拆数据存放在内存中,分拆数据与S1步骤中OBJ文件一一 对应,且能够按照拆分的逆过程进行重组,生成原始OBJ文件。
3. 根据权利要求2所述的一种针对编译中间结果的软件保护方法,其特征在于,S3步 骤的预处理主要进行以下方面分析: 531 :该分拆数据是否满足进行混淆的基本条件,对不达标的执行跳过混淆步骤;判断 标准来源于后续变形操作是否能够处理; 532 :针对S31得到的结果,搜集混淆操作需要的数据集合,存放到固定的内存位置,提 供给混淆模块访问; 533 :针对S32得到的分拆数据结果,根据混淆功能需要,调整分拆数据内容; 其中,S32和S33步骤的具体所做的工作包括: 1>根据模块工作内容的可行性,对其中的各种信息进行筛选,筛选的信息包括OBJ中 包含的函数节、数据节、重定位节,同时,根据混淆的类型,筛选出OBJ文件中能够进行混淆 的部分,不能够进行混淆的部分,并保存下来; 2>对函数节中包含的指令进行分析和处理,解析相对转移指令,对这部分指令进行 预处理,记录其相对跳转位置,同时,还处理和记录OBJ中包含的编译器对一些高级语言中 的特殊语句进行的优化块,跳过或者用特殊的处理方式处理这部分数据; 3>分析高级语言源代码中包含的全局变量定义位置和大小,以及相关联的数据,对这 部分数据进行存储,作为后续字符串加密的初始化数据。
4. 根据权利要求3所述的一种针对编译中间结果的软件保护方法,其特征在于,S4步 骤具体为: 541 :根据S33处理得到的分拆数据,提取其中系统函数调用的信息; 542 :对提取的调用进行筛选,保存下能够进行隐藏的系统调用,根据人的主动选择或 者随机算法的选择,来确定要进行下一步需要隐藏的系统调用,进行下一步操作; 543 :将S42得到的系统调用的指令进行解析,提取其中API名称和模块名称; 544 :根据S43得到的两部分数据,在进行系统调用的指令位置,将原始系统调用指令 替换成动态调用指令,动态调用方法为利用LoadLibrary加载该API所属动态库和利用 GetProcAddress获得函数地址,然后跳转到该函数地址; 545 :利用随机函数,在指令序列中随机添加冗余的系统调用; S46 :根据S33提供的分拆数据,找到引用字符串的指令以及字符串的存储位置,对能 够加密的字符串引用类型进行处理,该处理包括:1、找到字符串存储位置,对字符串加密, 2、插入对应加密方式的解密函数节,该函数节能够对传入的加密字符串进行解密,返回解 密的字符串,3、在引用该字符串的指令前插入对解密函数节的调用,使其在将字符串解密 之后,继续之前的指令流程。
5. 根据权利要求4所述的一种针对编译中间结果的软件保护方法,其特征在于,S5步 骤中进行针对普通指令的变形的具体步骤如下: 551 :实现一个膨胀引擎,将S33得到的分拆数据作为输入,对其中每一条指令进行判 断,并且作为下一步的输入; 552 :如果S51输出结果是常规指令,则根据配置对该常规指令进行替换,替换依据为 一个事先定义好的模板,模板里包含了针对一条指令的膨胀方式,配置包括膨胀引擎如何 选择模板以及运用几次模板; 553 :如果S51输出结果是控制转移指令,根据预处理得到的结果,进行下一步操作:如 果能够处理,则根据控制转移指令的类型,用能完成相同功能的指令替换原来的指令;如果 不能处理,则直接跳过执行。
6. 根据权利要求5所述的一种针对编译中间结果的软件保护方法,其特征在于,S6步 骤具体为:按照COFF文件标准以及拆分时的步骤进行重组,重组成OBJ文件,交给同一平台 的编译器配套使用的链接器,根据原始的链接参数进行链接,链接成可执行文件。
【文档编号】G06F21/14GK104091100SQ201410334357
【公开日】2014年10月8日 申请日期:2014年7月15日 优先权日:2014年7月15日
【发明者】张小松, 张艺峰, 牛伟纳, 陈瑞东, 王东, 杨高明, 于洲, 白金, 漆艳梅, 樊添 申请人:电子科技大学