word中若使被插入的文档不再和源文档产生联系,这种操作叫什么?

1. 一种报告文档生成方法所述方法包括: 获取报告文档生成请求; 根据所述报告文档生成请求,获取与所述报告文档生成请求对应的报告类型; 根据所述报告类型获取預存的所述报告类型对应的配置数据,所述配置数据包括与 所述报告类型对应的流程节点信息和报告模板; 根据所述流程节点信息和所述報告模板生成相应的报告文档。

2. 根据权利要求1所述的方法其特征在于,所述配置数据还包括与所述报告类型对应 的数据接口列表;所述根据所述流程节点信息和所述报告模板生成相应的报告文档,包 括: 根据所述数据接口列表获取相应的数据; 根据所述报告模板导入所述相应的数据; 对所述报告模板中的所述相应的数据进行处理,以生成相应的报告文档


( 桂林电子工业学院  计算机系 )
摘  要    当今盗版软件的泛滥成灾几乎已经成为了我们中国民族软件的灾难为了防止软件的非法复制、盗版,保护软件开发商的利益就必須对软件进行加密保护。现在市面上有许多反盗版软件但这类软件多是单机处理,并且只使用简单的加密手段很容易被解密者破解。
夲文描述了一个通过Internet集加密和电子注册于一身的完善的软件保护方案。该方案基于多种密码学意义上可靠的算法如对称加密算法,散列算法数字签名,密钥交换等等通过对Windows下PE可执行文件的结构及载入机制进行深刻的剖析, 巧妙的使用这些密码学算法及多种反破解方案對PE文件进行加密保护。
在该方案的实现中使用CryptoAPI中的数字签名算法RSA,加密算法RC2和RC4散列算法SHA,同时自己编写了使用了MD5算法用于快速计算大量数据的摘要;网络接口使用WinSocket;编程语言选用汇编语言和C++混合编程方式;反破解方案有检测文件完整性、检测代码完整性、反跟踪、反-反汇编、反Dump、代码变形等等
由于使用了可靠的密码学算法,使软件加密的强度大大提高;由于使用了Internet在线注册方式用户使用也非常方便。

winapi这些Winapi在C头文件中有声明。这些函数名和Windows自己声明的不同这里使用与标准WindowsAPI名字不同的函数主要有以下原因:在壳程序主体重包含了Windows頭文件,如果和标准WindowsAPI同名将产生符号名冲突;而如果不包含Windows标准头文件,壳程序中用到的许多Windows头文件中定义的符号将不得不从Windows头文件中複制到壳程序的头文件中这是一件很麻烦的事情,并且特别容易出错
对标准C库函数也做类似的处理,如 memcpy声明为iramemcpy,等等所不同的就昰:
iraMessageBoxA 实际上是我对系统 MessageBoxA 的转调,转调功能实现在用汇编语言写的代码中(见上一节)
而 iramemcpy(..) 函数是我自己用 C 语言写的,C++ 编译器将为它生成汇編代码
(3) 检测用户级调试器:在壳程序中测试跟踪标志,看是否被置位如果被置位,表示程序正在被跟踪主要代码如下:
当然,实现反跟踪还有多种方案如时间差反跟踪,设置SEH进行反跟踪等等
大部分加壳软件,它们加密了客户程序代码但自己的程序代码仍然是明攵,可以反汇编出来即使使用了花指令技术,仍然可以反汇编出来大部分指令并且壳程序中的字符串都是明文形式,很容易被识破進而更改。比如壳程序检验 SoftIce使用了一个字符串 "这样,用编辑器打开可执行程序就可以发现这个字符串的明文。如果它没有使用文件完整性检查那么解密者只需要把"\\.\ntice" 改成另外一个不存在的名字就可以了,比如 "abcdefghi"解密时字符个数一定要匹配,否则会出现文件中某项目文件偏移错误
 我加密了壳程序,当然加密过程是在Merge模块中实现的。这样我的壳程序中绝大多数代码的明文都不会出现。为了保证简洁加密壳程序自己的代码我用的是最简单的直接异或加密。只有极少的代码如解密壳程序的代码,必须以明文形式存在即函数 CryptNext,它本来┅共只有 20 条指令加入了花指令,多了差不多一倍
因为还有一点代码的明文在文件中,所以对这些明文,我使用了花指令使得其不能被反汇编。
我经过测试用这种方法加密的 notepad.exe,明文代码只有两条 !--壳程序入口的跳转代码!
实现起始也是比较简单的我的实现是这样嘚:定义两个函数:CryptAny,CryptNext用的是最简单的异或加密算法,CryptNext可以指定密钥CryptAny使用 MyShieldHeader中的一个域作为密钥,不能再指定密钥
这两个函数的代码嘟很短,都不到 20 行!
CryptAny 一般是对一个需要变形的代码区加密也可用于解密。
CryptNext 是对下一条指令开始的一个区块解密CryptNext中从堆栈中取出下一条指令的地址(在"call CryptNext"指令把它下一条指令的地址压入了堆栈),对该地址开始的区块进行解密因为CryptNext函数返回时将执行"call CryptNext"的下一条指令,所以该函数只能用于解密
我编写了一个宏,Anamorph它对一段代码进行变形,只需要把这段代码插入程序中并指定变形的终点。变形的起始点就是 Anamorph 嘚下一条指令
这使得几乎不可能跟踪,可以使 windasm 的跟踪功能不可用还没运行到原本用来检测 Trace Flag 代码的地方,就已经出现异常必须中止。
哽详细的请参考源程序
如果程序被跟踪,debugger 往往会在程序中写入 int 3 指令使得代码改变,而我怎么知道它在哪里写了 int 3 指令不知道!所以,峩只能计算代码的校验和最简单的方法就是把代码的所有内容按 32 位为单位加起来,计算出一个和存储在文件中(当然存储这个和地那個 32字节要跳过),运行时再重新计算看是否相等。这样是可行的 !我用了更安全的MD5 散列算法计算代码散列(为了速度仅计算壳程序的散列,而不是整个 PE Image 的散列--这已经足够了!)来检验
要计算代码的散列,那么所有的静态变量必须是只读的 !--我的代码块和数据块石绞囷在一个块中的。还有就是前面也说了,必须把文件中存储这个检验和部分跳过因为我还计算了文件的散列值,所以也应把文件的散列值跳过还有,windows 载入程序的时候会把把 API 的地址填入 IAT从而造成这些域的值发生改变,所以IAT也要跳过。
实现中我跳过了整个 MyShieldHeader 和整个 ImportTable因為这样实现起来更容易一点,而且决不会影响保护的强度 !
这一项比较关键因为,一般情况下Craker 只需要跟踪并在指令指针第一次落到客戶代码块时中断程序,这样他就得到了客户程序的入口地址然后再 Dump 内存,一切 Ok壳被他脱掉了 !
 所以,不能直接跳转到客户程序的入口我使用了一种技巧。多次往客户代码块中跳但是又可以准确的跳回来。是这样的:
 开始时我先将客户代码的入口压入堆栈,然后--
 我茬客户代码中查找 ret 指令机器码是 0xC3,查找到一个 ret就把该地址压入堆栈,直到客户代码块结束
 最后,执行一条 ret 指令这样,最后的这条 ret 指令将跳转到客户代码中的最后一个 ret 指令处而这个 ret 指令又跳到客户代码中的倒数第二个 ret 指令。如此反复客户代码块的第一条 ret 指令将把控制转移到客户代码的入口。
 值得说明的是在客户代码中找到的ret指令,并不一定就是一个 ret 指令因为有可能这样的指令中就含有一个 ret 指囹的机器码,如:
指令中就有4个ret 指令的机器码0xC3这些0c3h机器码在 ret 链中将被当作
指令执行,而在客户代码正常运行时却是当成
指令执行 !这对 Craker來说的确是一个很大的困惑
壳程序的一切必要的检查工作完成之后,就要载入Client的ImportTable这是勿庸置疑的。载入Client的ImportTable是一项比较复杂的工作要遍历整个ImportTable,一项一项的载入因为每个壳程序都要做这项工作,所以我也就不把如何载入Client的ImportTable作为重点详述下面只说明如何进行特殊的处悝以防止Cracker。
这里如果按普通的方法处理极容易被破解。因为程序运行起来以后所有客户块(Client Section)的内容都是明文形式的。这样程序运荇起来进入客户代码以后,解密者只需要把所有客户块的内存 Dump 出来并找到Client的入口(假设 7.4.6中 那串 ret指令的障碍已被他突破),然后再根据Dump的愙户的 ImportTable重建它即可。这样解密者不需要了解壳程序是如何工作的,就可以把壳脱掉
 其中还有问题,就是 Import中所有集合(数组字符串)的结束都是以 NULL 标志结尾,如 Function Name 和 Library Name 都是一个以 zero 字符结束的字符串加密的时候,可以先计算得到这个字符串的准确长度这是无疑的 !但是解密时就有问题,有可能在加密时把一个非 0 的字符加密成了 0这样,解密时计算这个字符串长度就会出错 次查找 !这样效率相差了 10 倍但昰,我自己编程载入 Function根本用不着 Hint,除非用更复杂的其它技术来提高效率 !
然而我的重点是加密而不是要效率。加密时我把函数名的的長度先计算出来存在 Hint 中,然后分别加密 Hint 和 Name注意,是分别加密不是一起加密 !原因不许多说。解密时先把 Hint 解密从中取得 Name 的长度,再紦 Name 解密
然而,对其它项的处理就不一样了如指向用来指向 IMAGE_IMPORT_BY_NAM 的一个指针(在PE文件中是一个偏移值),叫做 FirstThunk和OrignalFirstThunk这两个中只有一个有用 (其中详细内情请参阅参考文献[14])。是 DWORD即 4 个字节,如果这 4 个字节本不为 0而加密变成了 0,这样的概率是  1/2^32这么样的概率足以忽略了。并且也因为 没有其它可以存储它的地方,不过如果怕这个概率仍太大也有办法,就是把 TimeDateStamp 分成两个 Word一个 Word 存 Library Name 的长度,一个存该可执行文件从這个Library 导入的Function 个数
但是,对 ImageImportDescriptor就实在是没有地方放它的长度了,不过还好ImageImportDescriptor 足够大,有 20 字节这样,发生同类错误的概率是 1/2^160是绝对可以忽略的了,--因为散列表一般也只有 16 字节是基于忽略 1/2^128 的概率的。
解密时只要逆者这个方向解密就行了 !不在赘述
并且,解密时解密一項,导入一项清除一项 !但是 ImportAddressTable决不能清除 !--因为 Windows API调用是通过它的--只销毁其它项。
壳程序运行结束后会把控制转移到Client 程序,但是壳程序运行完把控制转移到Client 程序后,自己的代码已全部成了明文这样,解密者在 Client 程序运行后把壳程序所在的这个块 Dump 出来,然后就可以进行靜态分析了!
为了防止这样的 Cracker我在把控制转移到 Client 程序之前,也即壳程序运行的最后一条指令 ret 之前把壳程序自毁掉 !但是,当然自毁壳程程序的这段代码是不能自毁的谁能抓住自己的头发把自己提起来呢 ?我在自毁代码中也用到了花指令再次增加Cracker 的难度。代码自毁后這些花指令仍然存在继续迷惑 Cracker !
为了给 Craker 造成更大的困难,这段自毁代码我也使用了变形技术并且,为了给解密者留下最小的信息我使用了一个技巧,即先自毁"自毁代码"后面的代码然后再自毁"自毁代码"前面的代码,这样就只有两条指令不能自毁,即下面这两条紧挨著的指令不能自毁

安全散列算法计算 SN的散列 H,用自己的私人密钥 ASK对 H 进行数字签名得到 SNKey,把 SNKey 作为解密密码发给B同时也将自己的公钥 APK 发給 B。
校验也类似--然后将改过的 FAC 域写入文件 Q。
首先Register 验证 Q 的散列值,如果通过继续,否则认为文件收到损坏在这时可做一些处理(如洅验证一次,或提示 C 去向 B 换一套软件因为极有可能是光盘收到了物理损坏)。
服务器 Server 从数据库中查找这个 SN如果找到,并且这个序列号嘚拷贝已经注册并且 它收到的 SAC和以前注册的 SAC相同--相同的软件拷贝可以在一台被授权的计算机上多次安装/注册--或者找到了 SN,但该 SN 还未注册就将随 SN 一起发来的 SAC 存入数据库,待以后再验证这台计算机
接下来用 SHA 安全散列算法计算 SN的散列 H,再用自己的私人密钥 ASK对 H 进行数字签名嘚到 SNKey,把 SNKey 作为解密密码发给用户 C同时将自己的公钥 APK 也发给 C。
如果未找到该 SN提示客户端,可能有错误请求重发,多次错误之后可做一些处理(如认为是对服务器的恶意攻击不在理会从这台客户机上发来的信息)。
注册程序--运行在 C 的计算机上收到服务器返回的 SNKey 和 APK 之后 ,鼡 APK 对 SNKey 进行验证,验证过程是:用 APK 解密 SNKey得到 H',再用 SHA 安全散列算法计算 SN 的散列 H如果 H'等于 H,就通过了验证然后:
Register 程序用 SHA 安全散列算法计算 SNKey 嘚散列值 K1,一个密码算法把这个 K1 解掉同时用刚才得到的本地主机硬件信息的另一个散列值作为 LocalKey (得到 LocalKey 的散列算法和得到 SAC 的散列算法不同,但这用两个散列算法计算的输入数据是相同的--都是 C 的硬件信息HD)
然后, Register 再次计算 LocalKey 的 SHA 安全散列值 K2再用 K2 对 P 进行加密,在这个过程中使鼡SimpleCrypt算法和ComplexCrypt算法相结合和可以保证在解掉 K1并加上 K2 的过程中 P 的明文不出现在内存中。可以防止解密者在这个过程中 dump 内存
在上述步骤中,同时紦加密过的数据就写入了文件 R

软件开发者的服务器运行Server程序,命令行如下:
其中 datafile.dat 就是软件开发者指定的数据库文件(如果该文件不存在戓内容非法server将创建新文件),seller_port是用户 C 注册软件使用的端口user_port是销售处生成拷贝时向 Server 请求SNKey使用的端口 。
销售处如果要卖出一套软件执行洳下命令行:
其中p.exe是要加密的软件,serverIP是Server的IP 地址为销售处开放的端口,Q.exe 是输出文件
用户买到Q.exe,用如下命令行注册:
其中R.exe是最终的输出文件其余参数不用多说。
如果只在一台机器上演示那么:
如果要删除在本地生成的文件,请运行 delfiles.bat
为了达到预期的演示效果按如下步骤操作:
(1) 用UltraEdit打开R.exe ,修改其中一个字节再运行R.exe,会出现消息框提示发现自身被改动,拒绝运行
(2) 用WDASM反汇编R.exe ,可以发现只有入口的跳转指囹反汇编出来是对的,其余几乎所有的指令全部是错误的;用IDAPRO反汇编出来的错误更多
(3) 在WDASM中载入R.exe ,自动单步运行由于代码在运行中不断妀变自身,使得WDASM马上就出现了异常
(4) 把R.exe拷贝到另一台机子上,出现提示框提示一套软件只能在一台机器上,即注册的那台机器上运行
(5) 紦Q.exe拷贝到另一台机子上,运行注册程序会得到服务器发来的信息:"一个软件拷贝只能注册给一台机器"。生成的R.exe运行时会出现非法操作這正是我们预期的效果。
(6) 在正确的机器上再次运行注册程序会得到服务器发来的信息:"机器验证通过"。
(7) 运行SoftICE(正确和非正确的机器均可)再运行R.exe ,会出现消息框提示在内存中发现了SoftICE,并拒绝运行
(9) 在SoftICE中修改R的Shield块中一个地址的内容,如修改0x1010346的一个字节就会出现消息框,提示发现自身代码被改动并拒绝运行。
9 限制、不足与展望
任何事都不能做得完美无缺本软件当然也不例外。我个人认为本软件的設计思想比较好,但实现得不是很好
(3) 不能对有自校验功能的软件进行加密。因为有自校验功能的软件和本软件中的检验自身的MD5散列值基於同样的原理如果文件哪怕是有一位的改动,都会出现校验错误这类软件典型的是ReadBook,不过ReadBook在发现文件校验出错时仅给出提示而不阻圵用户使用。
(1) 对个别文件的加密可能会出现问题已知的是用本软件加密过的Winzip运行时会出现DLL版本不符的错误。
(2) 因为用本软件加密过的软件茬运行时要执行自校验、检查是否盗版、反跟踪、解密源程序代码、填入原程序的Import等操作使得被加密的软件载入速度会减慢,但载入后运行性能不受任何影响。在Celeron300A/192RAM/Windows2000环境下加密过的记事本程序速度慢了大约1秒钟,ACDsee慢了大约3秒钟在PIII733/192RAM/Windows2000环境下,打开记事本感觉不到速度的减慢
(3) 因为壳程序将控制转移到原程序后,原程序的代码(和数据)都变成了明文会被Cracker Dump内存,虽然我清除了原程序的ImportTable但仍可能被如下破解方案破解:
(4) Cracker扫描内存中所有的 DLL,得出其模块句柄再扫描其所有导入函数的 RVA,计算出 函数的真实地址在和这个已加密的程序的 IAT 中的函數地址比较,然后就可以得出IAT中相应项的ImportDescryptor和INT信息从而重建ImportTable 。整个软件就被破解了
(5) 在数字签名的实现过程中,如果被中间人攻击Merge(或Register)会收到不正确的SNKey和APK。如果在Merge和Server通信的过程中被中间人攻击会造成Server(即软件开发商)的注册数据库被中间人重新构造(相当于获取)。洳果中间人还在Register与Server通信过程中窃听可能会造成用户收到错误的SNKey而验证签名又通过,从而造成解密错误程序运行时因解密出错误的代码洏当机。这不会使软件开发商的软件被破解但会造成其信誉的损失。使用一些复杂的保密通信协议可以避免这种事情发生但因早期设計已经定型,后期又没有太多的时间只能留下这个遗憾了。
如果可能进行后续开发可以增加以下功能:
(1) 为防止Cracker 用上述 (3) 中的方法破解,鈳以使用"代码转移"技术即,把Client程序中的部分代码"转移"到Shield中而在原来的位置加入一条jmp指令跳转到转移的目标,如图 9-1:
但是如果在Client遇到轉移指令,问题就很麻烦我的设想了两个方案,但实现起来难度都很高
约束:只能处理立即数转移--即转移指令中不能有寄存器作为操莋数。因为如果有寄存器那就要对寄存器求值,这就相当于对程序的模拟执行复杂度很高。
还可以处理 call 指令即:
首先,把程序中所囿的"基本块"作为图的一个节点初始状态是只有从入口开始的那个"基本块"(编译原理术语),把它作为图的第一个顶点
在在遇到跳转指囹的时候,把转移的目标--也是一个基本块--作为下一个节点这即是图的"深度优先"遍历!
当然,要对指令的长度译码那就必须有整个机器指令集的操作码表,对要处理的转移指令还必须计算操作数(即转移的目标)等等。
写一个int 1(trace中断)例程获取每条指令执行后的机器狀态,从而可以获得正确的执行流程进而可以获得希望转移的代码块。同样这也需要整个机器指令集的操作码表,等等
(2) 将Merge和Register与Server的通信协议改为安全通信协议,可以用SSL协议也可以自己用TCP协议及CryptoAPI设计安全通信协议。阻止中间人攻击


(5) 此外,还可以使用其它一些更复杂的技术如Shield解密Client后把Client作为一个线程运行而自己仍在后台监视程序是否被跟踪,等等
(6) 最后,可以编写友好的图形界面更容易让用户使用,連接Server不用IP地址而用域名等等

通过对PE文件及Windows底层运作机制的深刻剖析,通过Internet用数字签名,散列密钥交换等可靠的密码学算法,对软件進行加密保护加密强度很高,破解难度也很高在有些方面甚至比一些商业加密软件还要出色。
由于在我国现阶段软件业的特殊需要加密软件以其特殊的作用,还将在今后很长一段时间中扮演知识产权保护者的角色加密与解密之间或是说反盗版与盗版之间的斗争还将繼续进行下去。我们的目标就是让加密技术在大部分时间里保持对解密技术的技术优势不断研究新型的加密方法,使解密的技术、时间、资源成本超出被保护软件的研制成本和实用时效从而在实际意义上保护软件在其生存周期中不被盗版。
本文是在古天龙教授和黄源老師的精心指导下完成的从论文的选题、文章结构的构筑到最后的定稿,都得到了古教授和黄老师的细心指点和提携古教授和黄老师严謹的治学作风让我受益匪浅。在此仅向古教授和黄老师致以最诚挚的谢意
本软件在开发过程中也受到古教授和黄老师的精心指导,在古敎授和黄老师的指导下克服了多个技术上难以逾越的障碍。
同时也向关心并支持我的家人、同学和热心的网友致以最衷心的感谢向我提出宝贵意见的网友有():
北京:机械工业出版社 ,2000 年1月 第1版
北京:电子工业出版社 2001 年 4 月
北京:清华大学出版社 ,1998 年 8 月
北京:人民邮電出版社 1997 年 7 月
5.  看雪 . 加密与解密--软件保护技术及完全解决方案 .
北京:电子工业出版社 ,2001 年9月第1版 
7.  吴功宜 徐敬东 韩毅刚 曹勇 . 16位/32位微处理器汇編语言程序设计 .
北京:国防工业出版社 1997 年 2 月第1版
北京:电子工业出版社 1997年3月第1版
北京:清华大学出版社 ,1996年
北京:电子工业出版社 1999 年 1 朤
武汉:华中科技大学出版社 ,2001 年
武汉:华中科技大学出版社 2001 年

我要回帖

 

随机推荐