混淆与加密——SmartAssembly的使用
本文目录 19 个章节
混淆与加密——SmartAssembly的使用
创建时间:2020/9/1 16:52
- 混淆与加密——SmartAssembly的使用
- 概念与功能
- 混淆功能
- 辅助功能
- 操作流程
- 1. 名称混淆 Obfuscation
- 类/方法名与字段名的混淆
- 转移方法 ChangeMethodParent
- 排除特定的命名空间
- 类/方法名与字段名的混淆
- 2. 流程混淆 Control Flow Obfuscation
- 3. 动态代理 References Dynamic Proxy
- 4. 字符串编码与加密 Strings Encoding
- 字符串统一收集编码 Encode
- 字符串压缩加密 Compress
- 字符串缓存 UseCache
- 使用类的内部委托获取字符串 UseImprovedEncoding
- 5. MSIL Disassembler Protection
- 密封
- 6. 实际应用建议
- Ref
- 概念与功能
概念与功能
混淆功能
名称混淆 Obfuscation 修改类型、字段、属性、方法等的名称。
流程混淆 Control Flow Obfuscation 修改方法内的执行逻辑,使其执行错综复杂。
动态代理 References Dynamic Proxy SmartAssembly 会将方法的调用转到动态代理上。
资源压缩加密 Resources Compression and Encryption SmartAssembly 会将资源以加密并压缩的方式嵌入到程序集中,运行时进行解压缩与解密。
字符串压缩加密 Strings Encoding SmartAssembly 会将字符串都进行加密,运行时自动对其进行解密。
防止 MSIL Disassembler 对其进行反编译 MSIL Disassembler Protection 在程序集中加一个 Attribute,这样 MSIL Disassembler 就不会反编译这个程序集。
辅助功能
强签名 Strong Name Signing 强签名可以确保程序之间的依赖关系是严格确定的,如果对其中的一个依赖进行篡改,将导致无法加载正确的程序集。 微软提供了强签名工具Sn.exe ,所以可以无需使用 SmartAssembly 的。
自动错误上报 Automated Error Reporting SmartAssembly 会自动向 exe 程序注入异常捕获与上报的逻辑。
功能使用率上报 Feature Usage Reporting SmartAssembly 会修改每个方法,记录这些方法的调用次数并上报。
依赖合并 Dependencies Merging SmartAssembly 会将程序集中你勾选的的依赖与此程序集合并成一个整的程序集。
依赖嵌入 Dependencies Embedding SmartAssembly 会将依赖以加密并压缩的方式嵌入到程序集中,运行时进行解压缩与解密。
裁剪 Pruning SmartAssembly 会将没有用到的字段、属性、方法、事件等删除。它声称删除了这些就能让程序逆向后代码更难读懂。
密封类 如果 SmartAssembly 发现一个类可以被密封,就会把它密封,这样能获得一点点性能提升。
生成调试信息 Generate Debugging Information 可以生成混淆后的 pdb 文件
操作流程
依赖SQL Server
new project
选择需要改进和保护的主程序集,可以为windows forms 应用程序、wpf应用程序、console 控制台程序、dll程序集、asp.net web 应用程序等等。
set destination。置经过smartassembly处理之后的程序集存放的位置。
设置smartassembly的程序参数。
dependencies merging,设置主程序集依靠的其他相关程序集。这些依靠的程序集会合并到主程序集中。建议不要选择第三方的程序集。
pruning 精简代码,这一步将从程序集中删除一些没有用的代码和元数据。smartassembly将检查程序集中的代码,自动判定哪些代码是有用的,哪些代码是从不会执行到的。同时,也将移除没有用的元数据。并可以分别选择对主程序集和依靠的程序集进行精简代码操作。
obfuscation 混淆代码。它将类和方法的名称更改为不可读的字符,但不会改变代码的功能,达到保护代码的目的。同时,也可以减少元数据,减小程序集的大小。
点击build 按钮,就输出混淆后的程序集了,并且所有依靠的程序集都合并在一起了,这样在发布的时候,只需要发布合并后的一个程序集和第三方的程序集了。
在smartassembly设置中,有个other protections 的选项,你可以选择在添加一些错误的metadata stream,如下图所示,这样在使用reflector 反编译工具根本就无法打开该程序集。
1. 名称混淆 Obfuscation
类/方法名与字段名的混淆
类名和方法名的混淆有三个不同级别:
等级 1 是使用 ASCII 字符集
等级 2 是使用不可见的 Unicode 字符集
等级 3 是使用高级重命名算法的不可见的 Unicode 字符集
需要注意:对于部分程序集,类与方法名(NameMangling)的等级只能选为 3,否则混淆程序会无法完成编译。
字段名的混淆有三个不同级别:
等级 1 是源码中字段名称和混淆后字段名称一一对应
等级 2 是在一个类中的不同字段使用不同名称即可(这不废话吗,不过 SmartAssembly 应该是为了强调与等级 1 和等级 3 的不同,必须写一个描述)
等级 3 是允许不同类中的字段使用相同的名字(这样能够更加让人难以理解)
需要注意:对于部分程序集,字段名(FieldsNameMangling)的等级只能选为 2 或 3,否则混淆程序会无法完成编译。 (实际试验中,以上各种组合经常会出现无法编译的情况。1~3 的差异对逆向来说难度差异非常小的) 该功能的源码:https://walterlv.gitee.io/post/algorithm-of-generating-random-identifiers
特别小心如果有 InternalsVisibleTo 或者依据名称的反射调用,这种混淆下极有可能挂掉!!!请充分测试你的软件,切记!!!
转移方法 ChangeMethodParent
如果开启,可能会将一个类中的方法转移到另一个类中,这使得逆向时对类型含义的解读更加匪夷所思。
排除特定的命名空间
如果你的程序集中确实存在需要被按照名称反射调用的类型,或者有 internal 的类/方法需要被友元程序集调用,请排除这些命名空间。
2. 流程混淆 Control Flow Obfuscation
0,1级差异不大 2,3级可读性降低,差异不大 4 级已经开始使用没有含义的指针来转换我们的内部实现了。这时除了外部调用以外,代码基本已无法解读其含义了。
3. 动态代理 References Dynamic Proxy
一些常规方法的调用被替换成了一个代理类的调用。生成的代理类都在根命名空间下。
4. 字符串编码与加密 Strings Encoding
字符串统一收集编码 Encode
将程序集中的 字符串 都统一收集起来,存为一个 资源 ;然后提供一个 解密辅助类 统一获取这些字符串。 生成字符串获取辅助类后,原本写着字符串的地方就会被替换为 Strings.Get(int) 方法的调用。
字符串压缩加密 Compress
压缩加密后,Resources 中的那份字符串资源成为二进制文件。这份压缩的字符串在程序启动的时候会进行一次解压,随后就直接读取解压后的字符串了。会占用启动时间(虽然不长),但不会占用太多运行时时间。
字符串缓存 UseCache
无论是否开启了字符串缓存,实际 Strings.Get 方法都会缓存字符串。
使用类的内部委托获取字符串 UseImprovedEncoding
之前的混淆都会在原来有字符串地方使用 Strings.Get 来获取字符串。而如果开启了这一选项,那么 Strings.Get 就不是全局调用的了,而是在类的内部调用一个委托字段。
5. MSIL Disassembler Protection
防止 MSIL Disassembler 对其进行反编译 这其实是个没啥用的选项,因为我们程序集只会多出一个全局的特性:[assembly: SuppressIldasm] 只有 MSIL Disassembler 和基于 MSIL Disassembler 的工具认这个特性。真正想逆向程序集的,根本不会在乎 MSIL Disassembler 被禁掉。 dnSpy 和 dotPeek 实际上都忽略了这个特性,依然能毫无障碍地反编译。
密封
在 OtherOptimizations 选项中,有一项 SealClasses 可以将所有可以密封的类进行密封(当然,此操作不会修改 API)。 公有类,可能被继承,不会被密封
6. 实际应用建议
原则: 在保证你没有额外产生 Bug,性能没有明显损失的情况下,进行最大程度的保护,能混淆得多厉害就混淆得多厉害。
流程混淆——建议必选 直接选用 4 级流程(不安全代码)混淆,如果出问题才换为 3 级(goto)混淆,理论上不需要使用更低级别 流程混淆对性能的影响是非常小的,因为多执行的代码都是有编译期级别优化的,没有太多性能开销的代码 流程混淆仅影响实现,不修改 API,所以基本不会影响其他程序各种对此程序集的调用
名称混淆——尽量选择 任意选择类/方法名和字段名的级别,只要能编译通过就行(因为无论选哪个,对程序的影响都一样,逆向的难度差异也较小) 名称混淆不影响程序执行性能,所以只要能打开,就尽量打开
如果有 InternalsVisibleTo 或者可能被其他程序集按名称反射调用,请:
* 关闭此混淆 * 使用 Exclude 排除特定命名空间,使此命名空间下的类/方法名不进行名称混淆 * 如果你能接受用 Attribute 标记某些类不应该混淆类名,也可以使用这些标记(只是我不推荐这么做,这让混淆污染了自己的代码)
动态代理——推荐选择 动态代理仅影响实现,不修改 API,所以基本不会影响其他程序各种对此程序集的调用 动态代理会生成新的类/委托来替换之前的方法调用,所以可能造成非常轻微的性能损失(一般可以忽略)
字符串压缩加密——可以选择 由于所有的字符串都被统一成一个资源,如果额外进行压缩加密,那么逆向时理解程序的含义将变得非常困难(没有可以参考的锚点) 会对启动时间有轻微的性能影响,如果额外压缩加密,那么会有更多性能影响;如果你对启动性能要求较高,还是不要选了 会轻微增加内存占用和读取字符串时的 CPU 占用,如果你对程序性能要求非常高,还是不要选了
其他功能意义不大: 裁剪 —— 一般也不会有多少开发者会故意往程序集中写一些不会用到的类吧! 依赖合并/依赖嵌入 —— 并不会对逆向造成障碍,开不开启差别不大,反而降低了性能 防止 MSIL Disassembler 反编译 —— 并不会对逆向造成障碍,防君子不防小人 密封类 —— 声称可以提升性能,但这点性能提升微乎其微
Ref
https://walterlv.gitee.io/post/obfuscation-configurations-of-smart-assembly.html https://www.cnblogs.com/yt9999/articles/2145989.html