using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; using System.Xml.Schema; using dnlib.DotNet; namespace Confuser.Core.Project { /// /// A module description in a Confuser project. /// public class ProjectModule { /// /// Initializes a new instance of the class. /// public ProjectModule() { Rules = new List(); } /// /// Gets the path to the module. /// public string Path { get; set; } /// /// Indicates whether this module is external and should not be obfuscated. /// public bool IsExternal { get; set; } /// /// Gets or sets the path to the strong name private key for signing. /// /// The path to the strong name private key, or null if not necessary. public string SNKeyPath { get; set; } /// /// Gets or sets the password of the strong name private key. /// /// The password of the strong name private key, or null if not necessary. public string SNKeyPassword { get; set; } /// /// Gets a list of protection rules applies to the module. /// /// A list of protection rules. public IList Rules { get; private set; } /// /// Resolves the module from the path. /// /// /// The base path for the relative module path, /// or null if the module path is absolute or relative to current directory. /// /// The resolved module's context. /// The resolved module. public ModuleDefMD Resolve(string basePath, ModuleContext context = null) { if (basePath == null) return ModuleDefMD.Load(Path, context); return ModuleDefMD.Load(System.IO.Path.Combine(basePath, Path), context); } /// /// Read the raw bytes of the module from the path. /// /// /// The base path for the relative module path, /// or null if the module path is absolute or relative to current directory. /// /// The loaded module. public byte[] LoadRaw(string basePath) { if (basePath == null) return File.ReadAllBytes(Path); return File.ReadAllBytes(System.IO.Path.Combine(basePath, Path)); } /// /// Saves the module description as XML element. /// /// The root XML document. /// The serialized module description. internal XmlElement Save(XmlDocument xmlDoc) { XmlElement elem = xmlDoc.CreateElement("module", ConfuserProject.Namespace); XmlAttribute nameAttr = xmlDoc.CreateAttribute("path"); nameAttr.Value = Path; elem.Attributes.Append(nameAttr); if (IsExternal) { XmlAttribute extAttr = xmlDoc.CreateAttribute("external"); extAttr.Value = IsExternal.ToString(); elem.Attributes.Append(extAttr); } if (SNKeyPath != null) { XmlAttribute snKeyAttr = xmlDoc.CreateAttribute("snKey"); snKeyAttr.Value = SNKeyPath; elem.Attributes.Append(snKeyAttr); } if (SNKeyPassword != null) { XmlAttribute snKeyPassAttr = xmlDoc.CreateAttribute("snKeyPass"); snKeyPassAttr.Value = SNKeyPassword; elem.Attributes.Append(snKeyPassAttr); } foreach (Rule i in Rules) elem.AppendChild(i.Save(xmlDoc)); return elem; } /// /// Loads the module description from XML element. /// /// The serialized module description. internal void Load(XmlElement elem) { Path = elem.Attributes["path"].Value; if (elem.Attributes["external"] != null) IsExternal = bool.Parse(elem.Attributes["external"].Value); else IsExternal = false; if (elem.Attributes["snKey"] != null) SNKeyPath = elem.Attributes["snKey"].Value.NullIfEmpty(); else SNKeyPath = null; if (elem.Attributes["snKeyPass"] != null) SNKeyPassword = elem.Attributes["snKeyPass"].Value.NullIfEmpty(); else SNKeyPassword = null; Rules.Clear(); foreach (XmlElement i in elem.ChildNodes.OfType()) { var rule = new Rule(); rule.Load(i); Rules.Add(rule); } } /// /// Returns a that represents this instance. /// /// A that represents this instance. public override string ToString() { return Path; } /// /// Clones this instance. /// /// A duplicated module. public ProjectModule Clone() { var ret = new ProjectModule(); ret.Path = Path; ret.IsExternal = IsExternal; ret.SNKeyPath = SNKeyPath; ret.SNKeyPassword = SNKeyPassword; foreach (var r in Rules) ret.Rules.Add(r.Clone()); return ret; } } /// /// Indicates add or remove the protection from the active protections /// public enum SettingItemAction { /// /// Add the protection to the active protections /// Add, /// /// Remove the protection from the active protections /// Remove } /// /// A setting within a rule. /// /// or public class SettingItem : Dictionary { /// /// Initialize this setting item instance /// /// The protection id /// The action to take public SettingItem(string id = null, SettingItemAction action = SettingItemAction.Add) { Id = id; Action = action; } /// /// The identifier of component /// /// The identifier of component. /// public string Id { get; set; } /// /// Gets or sets the action of component. /// /// The action of component. public SettingItemAction Action { get; set; } /// /// Saves the setting description as XML element. /// /// The root XML document. /// The setting module description. internal XmlElement Save(XmlDocument xmlDoc) { XmlElement elem = xmlDoc.CreateElement(typeof(T) == typeof(Packer) ? "packer" : "protection", ConfuserProject.Namespace); XmlAttribute idAttr = xmlDoc.CreateAttribute("id"); idAttr.Value = Id; elem.Attributes.Append(idAttr); if (Action != SettingItemAction.Add) { XmlAttribute pAttr = xmlDoc.CreateAttribute("action"); pAttr.Value = Action.ToString().ToLower(); elem.Attributes.Append(pAttr); } foreach (var i in this) { XmlElement arg = xmlDoc.CreateElement("argument", ConfuserProject.Namespace); XmlAttribute nameAttr = xmlDoc.CreateAttribute("name"); nameAttr.Value = i.Key; arg.Attributes.Append(nameAttr); XmlAttribute valAttr = xmlDoc.CreateAttribute("value"); valAttr.Value = i.Value; arg.Attributes.Append(valAttr); elem.AppendChild(arg); } return elem; } /// /// Loads the setting description from XML element. /// /// The serialized setting description. internal void Load(XmlElement elem) { Id = elem.Attributes["id"].Value; if (elem.Attributes["action"] != null) Action = (SettingItemAction)Enum.Parse(typeof(SettingItemAction), elem.Attributes["action"].Value, true); else Action = SettingItemAction.Add; Clear(); foreach (XmlElement i in elem.ChildNodes.OfType()) Add(i.Attributes["name"].Value, i.Attributes["value"].Value); } /// /// Clones this instance. /// /// A duplicated setting item. public SettingItem Clone() { var item = new SettingItem(Id, Action); foreach (var entry in this) item.Add(entry.Key, entry.Value); return item; } } /// /// A rule that control how s are applied to module /// public class Rule : List> { /// /// Initialize this rule instance /// /// The pattern /// The preset /// Inherits protection public Rule(string pattern = "true", ProtectionPreset preset = ProtectionPreset.None, bool inherit = false) { Pattern = pattern; Preset = preset; Inherit = inherit; } /// /// Gets or sets the pattern that determine the target components of the rule. /// /// The pattern expression. public string Pattern { get; set; } /// /// Gets or sets the protection preset this rule uses. /// /// The protection preset. public ProtectionPreset Preset { get; set; } /// /// Gets or sets a value indicating whether this inherits settings from earlier rules. /// /// true if it inherits settings; otherwise, false. public bool Inherit { get; set; } /// /// Saves the rule description as XML element. /// /// The root XML document. /// The serialized rule description. internal XmlElement Save(XmlDocument xmlDoc) { XmlElement elem = xmlDoc.CreateElement("rule", ConfuserProject.Namespace); XmlAttribute ruleAttr = xmlDoc.CreateAttribute("pattern"); ruleAttr.Value = Pattern; elem.Attributes.Append(ruleAttr); if (Preset != ProtectionPreset.None) { XmlAttribute pAttr = xmlDoc.CreateAttribute("preset"); pAttr.Value = Preset.ToString().ToLower(); elem.Attributes.Append(pAttr); } if (Inherit != true) { XmlAttribute attr = xmlDoc.CreateAttribute("inherit"); attr.Value = Inherit.ToString().ToLower(); elem.Attributes.Append(attr); } foreach (var i in this) elem.AppendChild(i.Save(xmlDoc)); return elem; } /// /// Loads the rule description from XML element. /// /// The serialized module description. internal void Load(XmlElement elem) { Pattern = elem.Attributes["pattern"].Value; if (elem.Attributes["preset"] != null) Preset = (ProtectionPreset)Enum.Parse(typeof(ProtectionPreset), elem.Attributes["preset"].Value, true); else Preset = ProtectionPreset.None; if (elem.Attributes["inherit"] != null) Inherit = bool.Parse(elem.Attributes["inherit"].Value); else Inherit = true; Clear(); foreach (XmlElement i in elem.ChildNodes.OfType()) { var x = new SettingItem(); x.Load(i); Add(x); } } /// /// Clones this instance. /// /// A duplicated rule. public Rule Clone() { var ret = new Rule(); ret.Preset = Preset; ret.Pattern = Pattern; ret.Inherit = Inherit; foreach (var i in this) { var item = new SettingItem(); item.Id = i.Id; item.Action = i.Action; foreach (string j in i.Keys) item.Add(j, i[j]); ret.Add(item); } return ret; } } /// /// The exception that is thrown when there exists schema errors in the project XML. /// public class ProjectValidationException : Exception { /// /// Initializes a new instance of the class. /// /// The list of schema exceptions. internal ProjectValidationException(List exceptions) : base(exceptions[0].Message) { Errors = exceptions; } /// /// Gets the schema exceptions. /// /// A list of schema exceptions. public IList Errors { get; private set; } } /// /// Represent a project of Confuser. /// public class ConfuserProject : List { /// /// The namespace of Confuser project schema /// public const string Namespace = "http://confuser.codeplex.com"; /// /// The schema of project XML. /// public static readonly XmlSchema Schema = XmlSchema.Read(typeof(ConfuserProject).Assembly.GetManifestResourceStream("Confuser.Core.Project.ConfuserPrj.xsd"), null); /// /// Initializes a new instance of the class. /// public ConfuserProject() { ProbePaths = new List(); PluginPaths = new List(); Rules = new List(); } /// /// Gets or sets the seed of pseudo-random generator used in process of protection. /// /// The random seed. public string Seed { get; set; } /// /// Gets or sets a value indicating whether debug symbols are generated. /// /// true if debug symbols are generated; otherwise, false. public bool Debug { get; set; } /// /// Gets or sets the output directory. /// /// The output directory. public string OutputDirectory { get; set; } /// /// Gets or sets the base directory of the project. /// /// The base directory. public string BaseDirectory { get; set; } /// /// Gets a list of protection rules that applies globally. /// /// A list of protection rules. public IList Rules { get; private set; } /// /// Gets or sets the packer used to pack up the output. /// /// The packer. public SettingItem Packer { get; set; } /// /// Gets a list of paths that used to resolve assemblies. /// /// The list of paths. public IList ProbePaths { get; private set; } /// /// Gets a list of paths to plugin. /// /// The list of plugins. public IList PluginPaths { get; private set; } /// /// Saves the project as XML document. /// /// The serialized project XML. public XmlDocument Save() { var xmlDoc = new XmlDocument(); xmlDoc.Schemas.Add(Schema); XmlElement elem = xmlDoc.CreateElement("project", Namespace); XmlAttribute outputAttr = xmlDoc.CreateAttribute("outputDir"); outputAttr.Value = OutputDirectory; elem.Attributes.Append(outputAttr); XmlAttribute baseAttr = xmlDoc.CreateAttribute("baseDir"); baseAttr.Value = BaseDirectory; elem.Attributes.Append(baseAttr); if (Seed != null) { XmlAttribute seedAttr = xmlDoc.CreateAttribute("seed"); seedAttr.Value = Seed; elem.Attributes.Append(seedAttr); } if (Debug) { XmlAttribute debugAttr = xmlDoc.CreateAttribute("debug"); debugAttr.Value = Debug.ToString().ToLower(); elem.Attributes.Append(debugAttr); } foreach (Rule i in Rules) elem.AppendChild(i.Save(xmlDoc)); if (Packer != null) elem.AppendChild(Packer.Save(xmlDoc)); foreach (ProjectModule i in this) elem.AppendChild(i.Save(xmlDoc)); foreach (string i in ProbePaths) { XmlElement path = xmlDoc.CreateElement("probePath", Namespace); path.InnerText = i; elem.AppendChild(path); } foreach (string i in PluginPaths) { XmlElement path = xmlDoc.CreateElement("plugin", Namespace); path.InnerText = i; elem.AppendChild(path); } xmlDoc.AppendChild(elem); return xmlDoc; } /// /// Loads the project from specified XML document. /// /// The XML document storing the project. /// /// The project XML contains schema errors. /// public void Load(XmlDocument doc) { doc.Schemas.Add(Schema); var exceptions = new List(); doc.Validate((sender, e) => { if (e.Severity != XmlSeverityType.Error) return; exceptions.Add(e.Exception); }); if (exceptions.Count > 0) { throw new ProjectValidationException(exceptions); } XmlElement docElem = doc.DocumentElement; OutputDirectory = docElem.Attributes["outputDir"].Value; BaseDirectory = docElem.Attributes["baseDir"].Value; if (docElem.Attributes["seed"] != null) Seed = docElem.Attributes["seed"].Value.NullIfEmpty(); else Seed = null; if (docElem.Attributes["debug"] != null) Debug = bool.Parse(docElem.Attributes["debug"].Value); else Debug = false; Packer = null; Clear(); ProbePaths.Clear(); PluginPaths.Clear(); Rules.Clear(); foreach (XmlElement i in docElem.ChildNodes.OfType()) { if (i.Name == "rule") { var rule = new Rule(); rule.Load(i); Rules.Add(rule); } else if (i.Name == "packer") { Packer = new SettingItem(); Packer.Load(i); } else if (i.Name == "probePath") { ProbePaths.Add(i.InnerText); } else if (i.Name == "plugin") { PluginPaths.Add(i.InnerText); } else { var asm = new ProjectModule(); asm.Load(i); Add(asm); } } } /// /// Clones this instance. /// /// A duplicated project. public ConfuserProject Clone() { var ret = new ConfuserProject(); ret.Seed = Seed; ret.Debug = Debug; ret.OutputDirectory = OutputDirectory; ret.BaseDirectory = BaseDirectory; ret.Packer = Packer == null ? null : Packer.Clone(); ret.ProbePaths = new List(ProbePaths); ret.PluginPaths = new List(PluginPaths); foreach (var module in this) ret.Add(module.Clone()); foreach (var r in Rules) ret.Rules.Add(r); return ret; } } }