Field Encryption
Encrypting field values can effectively prevent malicious attackers from using memory modification techniques to protect code security.
Supported Variable Types
Currently supports encryption of class static variables and member variables, but does not support encryption of ordinary temporary variables within functions. Supported field types for encryption are:
- int, uint
- long, ulong
- IntPtr, UIntPtr
- float
- double
Implementation Principle
- Before writing to a field, the value is encrypted using
EncryptionService<Scope>::Encrypt
- Before reading from a field, the value is decrypted using
EncryptionService<Scope>::Decrypt
Obfuz modifies all read and write operations to the encrypted field in the assembly, and this encryption process is completely transparent to the assembly. Obfuz's field encryption algorithm ensures that a value of 0 maps to 0, so no additional initialization is required for zero values.
Note that Obfuz only guarantees that encryption and decryption operations are performed when reading from or writing to encrypted fields in the code. If reflection is used, the encrypted values are accessed directly.
Compatibility with Serialization Libraries
For serialization libraries, the following scenarios exist:
- If code generation is used to serialize fields, both read and write operations will go through decryption and encryption, just like manually written code, so there will be no issues.
- If reflection is used to read Property-type fields, since the Property functions also perform decryption and encryption on the underlying data, there will be no issues.
- If reflection is used to save encrypted field data and then read the encrypted field data back via reflection, there will also be no issues.
- If reflection is used to save the original field values and then the fields are accessed directly in the code, the fields will not be encrypted, and the decryption operation during reading will result in incorrect data.
Taking Newtonsoft.Json as an example:
- If Newtonsoft.Json is first used to save object data to a JSON file and then deserialize the JSON file back into the object, there will be no issues.
- If a JSON file is manually written with unencrypted field values and then deserialized into an object, these unencrypted fields will be incorrectly decrypted, resulting in errors.
The same applies to MonoBehaviour and similar cases. If you set a field value in the inspector, since Unity uses reflection to obtain the field value, it saves and loads the original value. When you access the field in code, you will incorrectly get the decrypted value.
Settings
ObfuzSettings.FieldEncryptSettings
contains constant encryption related settings, detailed documentation can be found in Configuration.
Encryption Level
The encryption level affects the ops
parameter passed when calling EncryptionService<Scope>::Encrypt
. For detailed introduction to the ops parameter, see the documentation Encryption.
The encryption level value range is [1-4]
. During encryption, the number of ops generated equals the encryption level value. Simply enabling constant encryption can effectively prevent cracking. The encryption level size does not significantly improve the difficulty of anti-cracking, so it is recommended to default to 1.
The FieldEncryptSettings.EncryptionLevel
field can set the global default encryption level.
EncryptFieldAttribute
EncryptFieldAttribute provides a convenient way to mark fields as encrypted fields in code. For detailed documentation, see Obfuz CustomAttributes.
It has higher priority than Obfuscation Pass rules and [ObfuzIgnore]
. As long as a field has the [EncryptField]
attribute, it will still be encrypted even if the field and its containing type have the [ObfuzIgnore]
attribute.
Example code:
[ObfuzIgnore]
class A
{
[EncryptField]
public int x1; // Variable x1 will still be encrypted, ignoring [ObfuzIgnore] on the type
[ObfuzIgnore]
[EncryptField]
public int x2; // Variable x2 will still be encrypted, ignoring [ObfuzIgnore] on the field
public int y; // Variable y will not be encrypted, nor will it be subject to any obfuscation or encryption passes
}
Rule Files
Since field encryption affects field read/write performance, no fields are encrypted by default.
Since encrypted fields are generally very few, by design it has higher priority than Obfuscation Pass rules, but lower than [ObfuzIgnore]
.
Supports fine-grained control of field encryption scope and effects through rule files. The FieldEncryptSettings.RuleFiles
option can configure 0-N rule files.
Rule file relative paths are from the project directory, valid rule file paths look like: Assets/XXX/YYY.xml
.
Configuration example:
<?xml version="1.0" encoding="UTF-8"?>
<obfuz>
<assembly name="Obfus1">
<type name="*">
<field name="a"/>
<field name="b"/>
</type>
</assembly>
</obfuz>
- Top-level tag must be obfuz
- Second-level tags must be assembly
assembly
Attribute | Nullable | Default | Description |
---|---|---|---|
name | No | Assembly name, must be in the obfuscated assembly list |
Assembly's child elements can only be type.
type
Attribute | Nullable | Default | Description |
---|---|---|---|
name | No | Type name wildcard string, if empty means match all types. Nested types use / to separate the declaring type and enclosed subtype, like test.ClassA/ClassB . |
Since field encryption can only act on fields, type's child elements can only be field.
field
Attribute | Nullable | Default | Description |
---|---|---|---|
name | No | Name of the field to be encrypted. Field name wildcard string, if empty means match all types |