跳到主要内容

字段加密

加密字段值可以有效阻止恶意攻击者使用内存篡改之类的手段,保护代码安全。

支持的变量类型

目前支持类静态变量,成员变量的加密,但不支持函数内普通临时变量的加密。支持加密的字段类型有:

  • int、uint
  • long、ulong
  • IntPtr、UIntPtr
  • float
  • double

实现原理

  • 字段写入前先使用EncryptionService<Scope>::Encrypt对值进行加密
  • 字段读取前先使用EncryptionService<Scope>::Decrypt对值进行解密

Obfuz会修改所有程序集中对该加密字段的读取和写入操作,这个加密过程对程序集是完全无感的。Obfuz的字段加密算法会保证0值映射到0值, 因此无需对0值额外初始化。

注意

注意Obfuz只保证代码中读取和写入加密字段时会执行加解密操作,如果通过反射则直接操作的是加密后的值。

与序列化库的兼容性

对于序列化库有以下几种情况:

  • 如果使用生成代码的方式去序列化字段,读取和写入操作都会经过解密和加密操作,跟手写代码一样,完全不会有问题。
  • 如果使用反射方式读取Property类型的字段,由于Property函数读写底层数据也经过了解密和加密操作,也不会有问题。
  • 如果使用使用反射的方式去保存加密后字段数据,然后再用反射的方式读入加密后字段数据,也不会有问题。
  • 如果使用反射的方式保存字段原始值,然后在代码中直接访问字段,由于字段未经过加密,读取时执行了解密操作,最终数据是错误的。

以NewtonsoftJson为例:

  • 如果先用Newtonsoftjson保存对象数据到json文件,后续再反序列化json文件的数据到对象,不会有问题。
  • 如果直接手写一个json文件,字段值是未加密的值,反序列化json文件到对象,则这些未加密的字段经过额外解密后反而错了。

MonoBehaviour之类的也类似,如果你在inspector中设置了某字段的值,由于Unity使用反射获得字段值,它保存和加载操作的都是原始值。 当你在代码中访问该字段时,会错误地获得经过解密操作的值。

设置

ObfuzSettings.FieldEncryptSettings中包含常量加密相关设置,详细见文档设置

加密级别

加密级别影响调用EncryptionService<Scope>::Encrypt时传递的ops参数。关于ops参数的详细介绍可见文档加密

加密级别取值范围为[1-4],加密时会生成跟指令数与加密级别的值相同个数的ops。只要开启常量加密即可有效阻止破解,加密级别大小对反破解的难度提升不大,建议默认取1即可。

FieldEncryptSettings.EncryptionLevel字段可以设置全局默认加密级别。

EncryptFieldAttribute

EncryptFieldAttribute提供代码中便捷地标记某个字段为加密字段的办法,详细文档见Obfuz CustomAttributes

它的优先级高于Obfuscation Pass规则和[ObfuzIgnore]。只要对某个字段添加了[EncryptField]特性,即使字段及所在类型有[ObfuzIgnore]特性,仍然会被加密。

示例代码:


[ObfuzIgnore]
class A
{
[EncryptField]
public int x1; // 变量x1仍然会被加密,无视类型上的[ObfuzIgnore]

[ObfuzIgnore]
[EncryptField]
public int x2; // 变量x2仍然会被加密,无视字段上的[ObfuzIgnore]

public int y; // 变量y不会加密,也不会被释加任何混淆或加密Pass
}

规则文件

由于字段加密会影响字段的读写性能,因此默认情况下不会加密任何字段

由于被加密的字段一般只是极少数,因此设计上它的优先级高于Obfuscation Pass规则,但低于[ObfuzIgnore]

支持通过规则文件精细地控制字段加密的范围和效果。FieldEncryptSettings.RuleFiles选项中可以配置0-N个规则文件。 规则文件的相对路径为项目目录,有效的规则文件路径类似这样:Assets/XXX/YYY.xml

配置示例如下:


<?xml version="1.0" encoding="UTF-8"?>

<obfuz>
<assembly name="Obfus1">
<type name="*">
<field name="a"/>
<field name="b"/>
</type>
</assembly>
</obfuz>
  • 顶层tag必须是obfuz
  • 次级tag必须是assembly

assembly

属性可空默认值描述
name程序集名,必须在混淆程序集列表中

assembly的子元素只能为type。

type

属性可空默认值描述
name类型名的通配字符串,如果为空表示匹配所有类型。嵌套类型使用/划分声明所在类型和被包围的子类型,如test.ClassA/ClassB

由于字段加密只能作用于字段,因此type的子元素只能为field。

field

属性可空默认值描述
name被加密的字段名。字段名的通配字符串,如果为空表示匹配所有类型