新增const encrypt cache相关配置项

backup
walon 2025-05-09 11:03:05 +08:00
parent f4aa76ca39
commit 50dba09c6a
6 changed files with 129 additions and 46 deletions

View File

@ -43,6 +43,8 @@ namespace Obfuz.ObfusPasses.ConstObfus
protected override bool TryObfuscateInstruction(MethodDef method, Instruction inst, IList<Instruction> instructions, int instructionIndex, protected override bool TryObfuscateInstruction(MethodDef method, Instruction inst, IList<Instruction> instructions, int instructionIndex,
List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions) List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
{ {
bool currentInLoop = false;
ConstCachePolicy constCachePolicy = _dataObfuscatorPolicy.GetMethodConstCachePolicy(method);
switch (inst.OpCode.OperandType) switch (inst.OpCode.OperandType)
{ {
case OperandType.InlineI: case OperandType.InlineI:
@ -51,58 +53,59 @@ namespace Obfuz.ObfusPasses.ConstObfus
case OperandType.ShortInlineR: case OperandType.ShortInlineR:
case OperandType.InlineR: case OperandType.InlineR:
{ {
bool needCache = currentInLoop ? constCachePolicy.cacheConstInLoop : constCachePolicy.cacheConstNotInLoop;
object operand = inst.Operand; object operand = inst.Operand;
if (operand is int) if (operand is int)
{ {
int value = (int)operand; int value = (int)operand;
if (_dataObfuscatorPolicy.NeedObfuscateInt(method, value)) if (_dataObfuscatorPolicy.NeedObfuscateInt(method, currentInLoop, value))
{ {
_dataObfuscator.ObfuscateInt(method, value, outputInstructions); _dataObfuscator.ObfuscateInt(method, needCache, value, outputInstructions);
return true; return true;
} }
} }
else if (operand is sbyte) else if (operand is sbyte)
{ {
int value = (sbyte)operand; int value = (sbyte)operand;
if (_dataObfuscatorPolicy.NeedObfuscateInt(method, value)) if (_dataObfuscatorPolicy.NeedObfuscateInt(method, currentInLoop, value))
{ {
_dataObfuscator.ObfuscateInt(method, value, outputInstructions); _dataObfuscator.ObfuscateInt(method, needCache, value, outputInstructions);
return true; return true;
} }
} }
else if (operand is byte) else if (operand is byte)
{ {
int value = (byte)operand; int value = (byte)operand;
if (_dataObfuscatorPolicy.NeedObfuscateInt(method, value)) if (_dataObfuscatorPolicy.NeedObfuscateInt(method, currentInLoop, value))
{ {
_dataObfuscator.ObfuscateInt(method, value, outputInstructions); _dataObfuscator.ObfuscateInt(method, needCache, value, outputInstructions);
return true; return true;
} }
} }
else if (operand is long) else if (operand is long)
{ {
long value = (long)operand; long value = (long)operand;
if (_dataObfuscatorPolicy.NeedObfuscateLong(method, value)) if (_dataObfuscatorPolicy.NeedObfuscateLong(method, currentInLoop, value))
{ {
_dataObfuscator.ObfuscateLong(method, value, outputInstructions); _dataObfuscator.ObfuscateLong(method, needCache, value, outputInstructions);
return true; return true;
} }
} }
else if (operand is float) else if (operand is float)
{ {
float value = (float)operand; float value = (float)operand;
if (_dataObfuscatorPolicy.NeedObfuscateFloat(method, value)) if (_dataObfuscatorPolicy.NeedObfuscateFloat(method, currentInLoop, value))
{ {
_dataObfuscator.ObfuscateFloat(method, value, outputInstructions); _dataObfuscator.ObfuscateFloat(method, needCache, value, outputInstructions);
return true; return true;
} }
} }
else if (operand is double) else if (operand is double)
{ {
double value = (double)operand; double value = (double)operand;
if (_dataObfuscatorPolicy.NeedObfuscateDouble(method, value)) if (_dataObfuscatorPolicy.NeedObfuscateDouble(method, currentInLoop, value))
{ {
_dataObfuscator.ObfuscateDouble(method, value, outputInstructions); _dataObfuscator.ObfuscateDouble(method, needCache, value, outputInstructions);
return true; return true;
} }
} }
@ -112,9 +115,10 @@ namespace Obfuz.ObfusPasses.ConstObfus
{ {
//RuntimeHelpers.InitializeArray //RuntimeHelpers.InitializeArray
string value = (string)inst.Operand; string value = (string)inst.Operand;
if (_dataObfuscatorPolicy.NeedObfuscateString(method, value)) if (_dataObfuscatorPolicy.NeedObfuscateString(method, currentInLoop, value))
{ {
_dataObfuscator.ObfuscateString(method, value, outputInstructions); bool needCache = currentInLoop ? constCachePolicy.cacheStringInLoop : constCachePolicy.cacheStringNotInLoop;
_dataObfuscator.ObfuscateString(method, needCache, value, outputInstructions);
return true; return true;
} }
return false; return false;
@ -129,12 +133,13 @@ namespace Obfuz.ObfusPasses.ConstObfus
IField rvaField = (IField)prevInst.Operand; IField rvaField = (IField)prevInst.Operand;
FieldDef ravFieldDef = rvaField.ResolveFieldDefThrow(); FieldDef ravFieldDef = rvaField.ResolveFieldDefThrow();
byte[] data = ravFieldDef.InitialValue; byte[] data = ravFieldDef.InitialValue;
if (data != null && _dataObfuscatorPolicy.NeedObfuscateArray(method, data)) if (data != null && _dataObfuscatorPolicy.NeedObfuscateArray(method, currentInLoop, data))
{ {
// remove prev ldtoken instruction // remove prev ldtoken instruction
Assert.AreEqual(Code.Ldtoken, totalFinalInstructions[totalFinalInstructions.Count - 1].OpCode.Code); Assert.AreEqual(Code.Ldtoken, totalFinalInstructions[totalFinalInstructions.Count - 1].OpCode.Code);
totalFinalInstructions.RemoveAt(totalFinalInstructions.Count - 1); totalFinalInstructions.RemoveAt(totalFinalInstructions.Count - 1);
_dataObfuscator.ObfuscateBytes(method, data, outputInstructions); bool needCache = currentInLoop ? constCachePolicy.cacheStringInLoop : constCachePolicy.cacheStringNotInLoop;
_dataObfuscator.ObfuscateBytes(method, needCache, data, outputInstructions);
return true; return true;
} }
} }

View File

@ -40,8 +40,9 @@ namespace Obfuz.ObfusPasses.ConstObfus
return MetadataImporter.Instance.GetDefaultModuleMetadataImporter(method.Module); return MetadataImporter.Instance.GetDefaultModuleMetadataImporter(method.Module);
} }
public void ObfuscateInt(MethodDef method, int value, List<Instruction> obfuscatedInstructions) public void ObfuscateInt(MethodDef method, bool needCacheValue, int value, List<Instruction> obfuscatedInstructions)
{ {
int ops = GenerateEncryptionOperations(); int ops = GenerateEncryptionOperations();
int salt = GenerateSalt(); int salt = GenerateSalt();
int encryptedValue = _encryptor.Encrypt(value, ops, salt); int encryptedValue = _encryptor.Encrypt(value, ops, salt);
@ -55,7 +56,7 @@ namespace Obfuz.ObfusPasses.ConstObfus
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaInt)); obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaInt));
} }
public void ObfuscateLong(MethodDef method, long value, List<Instruction> obfuscatedInstructions) public void ObfuscateLong(MethodDef method, bool needCacheValue, long value, List<Instruction> obfuscatedInstructions)
{ {
int ops = GenerateEncryptionOperations(); int ops = GenerateEncryptionOperations();
int salt = GenerateSalt(); int salt = GenerateSalt();
@ -70,7 +71,7 @@ namespace Obfuz.ObfusPasses.ConstObfus
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaLong)); obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaLong));
} }
public void ObfuscateFloat(MethodDef method, float value, List<Instruction> obfuscatedInstructions) public void ObfuscateFloat(MethodDef method, bool needCacheValue, float value, List<Instruction> obfuscatedInstructions)
{ {
int ops = GenerateEncryptionOperations(); int ops = GenerateEncryptionOperations();
int salt = GenerateSalt(); int salt = GenerateSalt();
@ -85,7 +86,7 @@ namespace Obfuz.ObfusPasses.ConstObfus
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaFloat)); obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaFloat));
} }
public void ObfuscateDouble(MethodDef method, double value, List<Instruction> obfuscatedInstructions) public void ObfuscateDouble(MethodDef method, bool needCacheValue, double value, List<Instruction> obfuscatedInstructions)
{ {
int ops = GenerateEncryptionOperations(); int ops = GenerateEncryptionOperations();
int salt = GenerateSalt(); int salt = GenerateSalt();
@ -100,7 +101,7 @@ namespace Obfuz.ObfusPasses.ConstObfus
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaDouble)); obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaDouble));
} }
public void ObfuscateBytes(MethodDef method, byte[] value, List<Instruction> obfuscatedInstructions) public void ObfuscateBytes(MethodDef method, bool needCacheValue, byte[] value, List<Instruction> obfuscatedInstructions)
{ {
int ops = GenerateEncryptionOperations(); int ops = GenerateEncryptionOperations();
int salt = GenerateSalt(); int salt = GenerateSalt();
@ -118,7 +119,7 @@ namespace Obfuz.ObfusPasses.ConstObfus
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaBytes)); obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaBytes));
} }
public void ObfuscateString(MethodDef method, string value, List<Instruction> obfuscatedInstructions) public void ObfuscateString(MethodDef method, bool needCacheValue, string value, List<Instruction> obfuscatedInstructions)
{ {
//int ops = GenerateEncryptionOperations(); //int ops = GenerateEncryptionOperations();
//int salt = GenerateSalt(); //int salt = GenerateSalt();

View File

@ -7,17 +7,17 @@ namespace Obfuz.ObfusPasses.ConstObfus
{ {
public interface IDataObfuscator public interface IDataObfuscator
{ {
void ObfuscateInt(MethodDef method, int value, List<Instruction> obfuscatedInstructions); void ObfuscateInt(MethodDef method, bool needCacheValue, int value, List<Instruction> obfuscatedInstructions);
void ObfuscateLong(MethodDef method, long value, List<Instruction> obfuscatedInstructions); void ObfuscateLong(MethodDef method, bool needCacheValue, long value, List<Instruction> obfuscatedInstructions);
void ObfuscateFloat(MethodDef method, float value, List<Instruction> obfuscatedInstructions); void ObfuscateFloat(MethodDef method, bool needCacheValue, float value, List<Instruction> obfuscatedInstructions);
void ObfuscateDouble(MethodDef method, double value, List<Instruction> obfuscatedInstructions); void ObfuscateDouble(MethodDef method, bool needCacheValue, double value, List<Instruction> obfuscatedInstructions);
void ObfuscateString(MethodDef method, string value, List<Instruction> obfuscatedInstructions); void ObfuscateString(MethodDef method, bool needCacheValue, string value, List<Instruction> obfuscatedInstructions);
void ObfuscateBytes(MethodDef method, byte[] value, List<Instruction> obfuscatedInstructions); void ObfuscateBytes(MethodDef method, bool needCacheValue, byte[] value, List<Instruction> obfuscatedInstructions);
void Done(); void Done();
} }

View File

@ -7,20 +7,30 @@ using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.ConstObfus namespace Obfuz.ObfusPasses.ConstObfus
{ {
public struct ConstCachePolicy
{
public bool cacheConstInLoop;
public bool cacheConstNotInLoop;
public bool cacheStringInLoop;
public bool cacheStringNotInLoop;
}
public interface IObfuscationPolicy public interface IObfuscationPolicy
{ {
bool NeedObfuscateMethod(MethodDef method); bool NeedObfuscateMethod(MethodDef method);
bool NeedObfuscateInt(MethodDef method, int value); ConstCachePolicy GetMethodConstCachePolicy(MethodDef method);
bool NeedObfuscateLong(MethodDef method, long value); bool NeedObfuscateInt(MethodDef method, bool currentInLoop, int value);
bool NeedObfuscateFloat(MethodDef method, float value); bool NeedObfuscateLong(MethodDef method, bool currentInLoop, long value);
bool NeedObfuscateDouble(MethodDef method, double value); bool NeedObfuscateFloat(MethodDef method, bool currentInLoop, float value);
bool NeedObfuscateString(MethodDef method, string value); bool NeedObfuscateDouble(MethodDef method, bool currentInLoop, double value);
bool NeedObfuscateArray(MethodDef method, byte[] array); bool NeedObfuscateString(MethodDef method, bool currentInLoop, string value);
bool NeedObfuscateArray(MethodDef method, bool currentInLoop, byte[] array);
} }
} }

View File

@ -35,9 +35,15 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
public bool? encryptDouble; public bool? encryptDouble;
public bool? encryptArray; public bool? encryptArray;
public bool? encryptString; public bool? encryptString;
public bool? encryptConstInLoop;
public bool? encryptStringInLoop;
public bool? cacheConstInLoop; public bool? cacheConstInLoop;
public bool? cacheConstNotInLoop; public bool? cacheConstNotInLoop;
public bool? cacheStringInLoop;
public bool? cacheStringNotInLoop; public bool? cacheStringNotInLoop;
public HashSet<int> notEncryptInts = new HashSet<int>(); public HashSet<int> notEncryptInts = new HashSet<int>();
public HashSet<long> notEncryptLongs = new HashSet<long>(); public HashSet<long> notEncryptLongs = new HashSet<long>();
public HashSet<string> notEncryptStrings = new HashSet<string>(); public HashSet<string> notEncryptStrings = new HashSet<string>();
@ -64,10 +70,18 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
encryptArray = parentRule.encryptArray; encryptArray = parentRule.encryptArray;
if (encryptString == null) if (encryptString == null)
encryptString = parentRule.encryptString; encryptString = parentRule.encryptString;
if (encryptConstInLoop == null)
encryptConstInLoop = parentRule.encryptConstInLoop;
if (encryptStringInLoop == null)
encryptStringInLoop = parentRule.encryptStringInLoop;
if (cacheConstInLoop == null) if (cacheConstInLoop == null)
cacheConstInLoop = parentRule.cacheConstInLoop; cacheConstInLoop = parentRule.cacheConstInLoop;
if (cacheConstNotInLoop == null) if (cacheConstNotInLoop == null)
cacheConstNotInLoop = parentRule.cacheConstNotInLoop; cacheConstNotInLoop = parentRule.cacheConstNotInLoop;
if (cacheStringInLoop == null)
cacheStringInLoop = parentRule.cacheStringInLoop;
if (cacheStringNotInLoop == null) if (cacheStringNotInLoop == null)
cacheStringNotInLoop = parentRule.cacheStringNotInLoop; cacheStringNotInLoop = parentRule.cacheStringNotInLoop;
@ -114,8 +128,11 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
encryptDouble = true, encryptDouble = true,
encryptArray = true, encryptArray = true,
encryptString = true, encryptString = true,
encryptConstInLoop = true,
encryptStringInLoop = true,
cacheConstInLoop = true, cacheConstInLoop = true,
cacheConstNotInLoop = false, cacheConstNotInLoop = false,
cacheStringInLoop = true,
cacheStringNotInLoop = true, cacheStringNotInLoop = true,
}; };
@ -276,6 +293,15 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
{ {
rule.encryptString = ParseBool(ele.GetAttribute("encryptString")); rule.encryptString = ParseBool(ele.GetAttribute("encryptString"));
} }
if (ele.HasAttribute("encryptConstInLoop"))
{
rule.encryptConstInLoop = ParseBool(ele.GetAttribute("encryptConstInLoop"));
}
if (ele.HasAttribute("encryptStringInLoop"))
{
rule.encryptStringInLoop = ParseBool(ele.GetAttribute("encryptStringInLoop"));
}
if (ele.HasAttribute("cacheConstInLoop")) if (ele.HasAttribute("cacheConstInLoop"))
{ {
rule.cacheConstInLoop = ParseBool(ele.GetAttribute("cacheConstInLoop")); rule.cacheConstInLoop = ParseBool(ele.GetAttribute("cacheConstInLoop"));
@ -284,6 +310,10 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
{ {
rule.cacheConstNotInLoop = ParseBool(ele.GetAttribute("cacheConstNotInLoop")); rule.cacheConstNotInLoop = ParseBool(ele.GetAttribute("cacheConstNotInLoop"));
} }
if (ele.HasAttribute("cacheStringInLoop"))
{
rule.cacheStringInLoop = ParseBool(ele.GetAttribute("cacheStringInLoop"));
}
if (ele.HasAttribute("cacheStringNotInLoop")) if (ele.HasAttribute("cacheStringNotInLoop"))
{ {
rule.cacheStringNotInLoop = ParseBool(ele.GetAttribute("cacheStringNotInLoop")); rule.cacheStringNotInLoop = ParseBool(ele.GetAttribute("cacheStringNotInLoop"));
@ -509,13 +539,29 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
return rule.disableEncrypt != true; return rule.disableEncrypt != true;
} }
public override bool NeedObfuscateInt(MethodDef method, int value) public override ConstCachePolicy GetMethodConstCachePolicy(MethodDef method)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
return new ConstCachePolicy
{
cacheConstInLoop = rule.cacheConstInLoop.Value,
cacheConstNotInLoop = rule.cacheConstNotInLoop.Value,
cacheStringInLoop = rule.cacheStringInLoop.Value,
cacheStringNotInLoop = rule.cacheStringNotInLoop.Value,
};
}
public override bool NeedObfuscateInt(MethodDef method, bool currentInLoop, int value)
{ {
ObfuscationRule rule = GetMethodObfuscationRule(method); ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptInt == false) if (rule.encryptInt == false)
{ {
return false; return false;
} }
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
if (rule.notEncryptInts.Contains(value)) if (rule.notEncryptInts.Contains(value))
{ {
return false; return false;
@ -535,13 +581,17 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
return true; return true;
} }
public override bool NeedObfuscateLong(MethodDef method, long value) public override bool NeedObfuscateLong(MethodDef method, bool currentInLoop, long value)
{ {
ObfuscationRule rule = GetMethodObfuscationRule(method); ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptLong == false) if (rule.encryptLong == false)
{ {
return false; return false;
} }
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
if (rule.notEncryptLongs.Contains(value)) if (rule.notEncryptLongs.Contains(value))
{ {
return false; return false;
@ -561,13 +611,17 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
return true; return true;
} }
public override bool NeedObfuscateFloat(MethodDef method, float value) public override bool NeedObfuscateFloat(MethodDef method, bool currentInLoop, float value)
{ {
ObfuscationRule rule = GetMethodObfuscationRule(method); ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptFloat == false) if (rule.encryptFloat == false)
{ {
return false; return false;
} }
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
foreach (var range in rule.notEncryptFloatRanges) foreach (var range in rule.notEncryptFloatRanges)
{ {
if (range.min != null && value < range.min) if (range.min != null && value < range.min)
@ -583,13 +637,17 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
return true; return true;
} }
public override bool NeedObfuscateDouble(MethodDef method, double value) public override bool NeedObfuscateDouble(MethodDef method, bool currentInLoop, double value)
{ {
ObfuscationRule rule = GetMethodObfuscationRule(method); ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptDouble == false) if (rule.encryptDouble == false)
{ {
return false; return false;
} }
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
foreach (var range in rule.notEncryptDoubleRanges) foreach (var range in rule.notEncryptDoubleRanges)
{ {
if (range.min != null && value < range.min) if (range.min != null && value < range.min)
@ -605,13 +663,17 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
return true; return true;
} }
public override bool NeedObfuscateString(MethodDef method, string value) public override bool NeedObfuscateString(MethodDef method, bool currentInLoop, string value)
{ {
ObfuscationRule rule = GetMethodObfuscationRule(method); ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptString == false) if (rule.encryptString == false)
{ {
return false; return false;
} }
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
if (rule.notEncryptStrings.Contains(value)) if (rule.notEncryptStrings.Contains(value))
{ {
return false; return false;
@ -631,13 +693,17 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
return true; return true;
} }
public override bool NeedObfuscateArray(MethodDef method, byte[] array) public override bool NeedObfuscateArray(MethodDef method, bool currentInLoop, byte[] array)
{ {
ObfuscationRule rule = GetMethodObfuscationRule(method); ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptArray == false) if (rule.encryptArray == false)
{ {
return false; return false;
} }
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
foreach (var range in rule.notEncryptArrayLengthRanges) foreach (var range in rule.notEncryptArrayLengthRanges)
{ {
if (range.min != null && array.Length < range.min) if (range.min != null && array.Length < range.min)

View File

@ -5,11 +5,12 @@ namespace Obfuz.ObfusPasses.ConstObfus.Policies
public abstract class ObfuscationPolicyBase : IObfuscationPolicy public abstract class ObfuscationPolicyBase : IObfuscationPolicy
{ {
public abstract bool NeedObfuscateMethod(MethodDef method); public abstract bool NeedObfuscateMethod(MethodDef method);
public abstract bool NeedObfuscateDouble(MethodDef method, double value); public abstract ConstCachePolicy GetMethodConstCachePolicy(MethodDef method);
public abstract bool NeedObfuscateFloat(MethodDef method, float value); public abstract bool NeedObfuscateDouble(MethodDef method, bool currentInLoop, double value);
public abstract bool NeedObfuscateInt(MethodDef method, int value); public abstract bool NeedObfuscateFloat(MethodDef method, bool currentInLoop, float value);
public abstract bool NeedObfuscateLong(MethodDef method, long value); public abstract bool NeedObfuscateInt(MethodDef method, bool currentInLoop, int value);
public abstract bool NeedObfuscateString(MethodDef method, string value); public abstract bool NeedObfuscateLong(MethodDef method, bool currentInLoop, long value);
public abstract bool NeedObfuscateArray(MethodDef method, byte[] array); public abstract bool NeedObfuscateString(MethodDef method, bool currentInLoop, string value);
public abstract bool NeedObfuscateArray(MethodDef method, bool currentInLoop, byte[] array);
} }
} }