深入探究 PowerBuilder .NET 程序集背后的幕布
检查生成的 CIL 并得出一些结论
.NET 平台吸引人的地方在于,所有代码,无论用何种语言或语法编写,都会编译成通用中间语言 (CIL),然后由通用语言运行时 (CLR) 进行管理和执行。PowerScript 代码在成为 CIL 之前需要经过一个两步过程。首先,它会通过 PBCS.exe 运行,将 PowerScript 转换为等效的 C# 代码。然后,生成的 CS 代码由 C# 编译器 CS.EXE 编译成标准的 CIL。
.NET SDK 附带的工具之一是 CIL 反汇编器 ILDASM.exe。该工具允许您探索生成的程序集并检查其 CIL 指令和元数据。
我决定构建一个简单的 C# 应用程序并将其编译成程序集,然后探索其 CIL,并用 PowerScript 重现它,以比较编译器输出并从中学习。
列表 1 显示了一个简单的 C# 计算器应用程序的源代码。它被编译成一个名为 calc.exe 的程序集。列表 2 显示了 add()
方法的 CIL。请注意两个 int32 参数以及生成的代码大小为 9 行。(本文的列表可以 在此处 下载。)
PowerBuilder 代码全部构建在应用程序对象中。Open 事件调用 add 方法,该方法将两个参数的值相加。请注意,我传递的值是整数。
图 1 显示了应用程序对象中的 PowerScript add 方法,列表 3 显示了由此生成的 CIL。这里有几点令人惊讶。首先,看看这一行代码生成的代码量:足足有 23 行代码;这个例程比其 C# 等效代码大 2.5 倍以上。其次,看看参数和返回类型:它们不是 int32,而是 Sybase PowerBuilder.PBInt 值对象。
仔细查看 CIL,您会发现大量的类型转换正在进行。首先,两个整数参数被转换为 long,然后总和从 long 转换回整数。为什么?答案在于 PowerBuilder 的算术运算方式与 C# 的算术运算方式不同。摘录自 PowerBuilder 在线帮助的“算术运算符”部分解释了您所看到的:“整数在计算中被提升为 long,在结果显式赋值给整数变量之前不会发生回绕。” PBCS.exe 过程生成的额外代码确保了与经典代码 100% 的兼容性。生成的代码模拟了在运算前将整数数据类型提升为 long,然后在将结果转换回整数。
为了简化我的 PowerScript 代码,我将 add 方法的返回类型和参数类型更改为 long。列表 4 显示了生成的 CIL。请注意,代码现在只有 8 条指令,比 C# 代码少一条。
从这次练习中我得出的结论是,那些希望在 .NET 环境中获得关键 PowerScript 例程最佳性能的人,最好看看他们的代码在 CIL 中是什么样的,并采取措施根据核心 PowerScript 规则使其尽可能高效。
躲避通缉犯
在我进行 ILDASM 练习的过程中,我很快意识到,由 PowerBuilder .NET 生成的任何代码,就像由任何符合通用语言规范 (CLS) 的语言生成的 CIL 代码一样,都可以被任何拥有广泛可用工具的人完全反汇编。任何专有的算法和流程在部署后都会显而易见且易于解码。现在需要的是一种方法来隐藏代码,使其不被好奇的眼睛看到。好消息是,由于 PowerScript .NET 是 CIL 兼容的,因此有许多基于标准且广泛使用的混淆(代码隐藏)工具可供选择。我的 TeamSybase 同事 Dave Fish 建议我尝试 LogicNP Software 的 Crypto Obfuscator for .NET。有关该产品功能的完整讨论,请参见 http://www.ssware.com/cryptoobfuscator/features.htm。Crypto Obfuscator 具有命令行界面,在通过目标项目的 Post Build 选项卡上的条目成功完成完整构建后,可以直接从 PowerBuilder IDE 调用。
在我的测试中,我使用了一个更庞大、更典型的 PowerBuilder 客户端/服务器应用程序。该应用程序包含大量 DataWindow、业务逻辑和数据访问逻辑。由于我是混淆新手,我使用了工具的用户界面来选择我的选项。图 2 显示了 CryptoObfuscator v2010 的用户界面以及我选择的选项。混淆后的代码会生成在应用程序的 bin\release 文件夹下的 CryptoObfuscator_Output 文件夹中。在备份了我的原始代码后,我将混淆后的 EXE 程序集复制到 release 文件夹中。图 3 显示了当我尝试反汇编混淆后的代码时发生的情况。最重要的是,应用程序运行正常,没有任何可察觉的缺陷。运行时能够正确执行混淆后的代码。一个重要的注意事项:DataWindow 对象经过加载图像优化并打包到一个名为 <yourrapp>.resources.dll 的程序集中。我尝试混淆此程序集导致一些 DataWindow 出现故障。经过仔细检查,不混淆资源 DLL 似乎不是安全风险,因为其中打包的 DataWindow 对象源在反汇编器中不可见。但是,由于代码安全是一个重要问题,在得出关于混淆最佳实践的结论之前,需要使用更广泛的工具和方法进行进一步研究。
摘要
通过以上所有内容,您现在可以理解,深入了解从 PowerBuilder .NET 应用程序生成的 CIL 代码如何帮助您微调算法以实现更高效的运行时性能。您还应该意识到,为什么代码混淆是必须的,以帮助您应对应用程序分发中可能出现的安全风险。