diff --git a/Confuser.Core/Project/ConfuserProject.cs b/Confuser.Core/Project/ConfuserProject.cs index d6307fa..40f220f 100644 --- a/Confuser.Core/Project/ConfuserProject.cs +++ b/Confuser.Core/Project/ConfuserProject.cs @@ -89,7 +89,7 @@ namespace Confuser.Core.Project { if (IsExternal) { XmlAttribute extAttr = xmlDoc.CreateAttribute("external"); - extAttr.Value = IsExternal.ToString(); + extAttr.Value = IsExternal ? "true" : "false"; elem.Attributes.Append(extAttr); } if (SNKeyPath != null) { @@ -610,4 +610,4 @@ namespace Confuser.Core.Project { return ret; } } -} \ No newline at end of file +} diff --git a/Confuser.MSBuild.Tasks/ConfuseTask.cs b/Confuser.MSBuild.Tasks/ConfuseTask.cs new file mode 100644 index 0000000..b59cb79 --- /dev/null +++ b/Confuser.MSBuild.Tasks/ConfuseTask.cs @@ -0,0 +1,33 @@ +using System.IO; +using System.Xml; +using Confuser.Core; +using Confuser.Core.Project; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Confuser.MSBuild.Tasks { + public sealed class ConfuseTask : Task { + [Required] + public ITaskItem Project { get; set; } + + [Required, Output] + public ITaskItem OutputAssembly { get; set; } + + public override bool Execute() { + var project = new ConfuserProject(); + var xmlDoc = new XmlDocument(); + xmlDoc.Load(Project.ItemSpec); + project.Load(xmlDoc); + project.OutputDirectory = Path.GetDirectoryName(OutputAssembly.ItemSpec); + + var logger = new MSBuildLogger(Log); + var parameters = new ConfuserParameters { + Project = project, + Logger = logger + }; + + ConfuserEngine.Run(parameters).Wait(); + return !logger.HasError; + } + } +} diff --git a/Confuser.MSBuild.Tasks/Confuser.MSBuild.Tasks.csproj b/Confuser.MSBuild.Tasks/Confuser.MSBuild.Tasks.csproj new file mode 100644 index 0000000..bf70465 --- /dev/null +++ b/Confuser.MSBuild.Tasks/Confuser.MSBuild.Tasks.csproj @@ -0,0 +1,59 @@ + + + + + + net461;netstandard2.0 + true + ..\ConfuserEx.snk + + + + Confuser.MSBuild + https://github.com/mkaring/ConfuserEx/blob/master/LICENSE.md + https://github.com/mkaring/ConfuserEx + Obfuscation Confuser ConfuserEx + true + false + true + true + $(TargetsForTfmSpecificContentInPackage);IncludeConfuserDependencyFiles + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Confuser.MSBuild.Tasks/CreateProjectTask.cs b/Confuser.MSBuild.Tasks/CreateProjectTask.cs new file mode 100644 index 0000000..0de560f --- /dev/null +++ b/Confuser.MSBuild.Tasks/CreateProjectTask.cs @@ -0,0 +1,84 @@ +using System; +using System.IO; +using System.Linq; +using System.Xml; +using Confuser.Core.Project; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Confuser.MSBuild.Tasks { + public sealed class CreateProjectTask : Task { + public ITaskItem SourceProject { get; set; } + + [Required] + public ITaskItem[] References { get; set; } + + [Required] + public ITaskItem AssemblyPath { get; set; } + + public ITaskItem[] SatelliteAssemblyPaths { get; set; } + + public ITaskItem KeyFilePath { get; set; } + + [Required, Output] + public ITaskItem ResultProject { get; set; } + + public override bool Execute() { + var project = new ConfuserProject(); + if (!string.IsNullOrWhiteSpace(SourceProject?.ItemSpec)) { + var xmlDoc = new XmlDocument(); + xmlDoc.Load(SourceProject.ItemSpec); + project.Load(xmlDoc); + + // Probe Paths are not required, because all dependent assemblies are added as external modules. + project.ProbePaths.Clear(); + } + + project.BaseDirectory = Path.GetDirectoryName(AssemblyPath.ItemSpec); + var mainModule = GetOrCreateProjectModule(project, AssemblyPath.ItemSpec); + if (!string.IsNullOrWhiteSpace(KeyFilePath?.ItemSpec)) { + mainModule.SNKeyPath = KeyFilePath.ItemSpec; + } + + if (SatelliteAssemblyPaths != null) { + foreach (var satelliteAssembly in SatelliteAssemblyPaths) { + if (!string.IsNullOrWhiteSpace(satelliteAssembly?.ItemSpec)) { + var satelliteModule = GetOrCreateProjectModule(project, satelliteAssembly.ItemSpec); + if (!string.IsNullOrWhiteSpace(KeyFilePath?.ItemSpec)) { + satelliteModule.SNKeyPath = KeyFilePath.ItemSpec; + } + } + } + } + + foreach (var probePath in References.Select(r => Path.GetDirectoryName(r.ItemSpec)).Distinct()) { + project.ProbePaths.Add(probePath); + } + + project.Save().Save(ResultProject.ItemSpec); + + return true; + } + + private static ProjectModule GetOrCreateProjectModule(ConfuserProject project, string assemblyPath, bool isExternal = false) { + var assemblyFileName = Path.GetFileName(assemblyPath); + var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); + foreach (var module in project) { + if (string.Equals(module.Path, assemblyFileName) || string.Equals(module.Path, assemblyName)) { + return module; + } + } + + if (assemblyPath.StartsWith(project.BaseDirectory)) { + assemblyPath = assemblyPath.Substring(project.BaseDirectory.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + } + + var result = new ProjectModule { + Path = assemblyPath, + IsExternal = isExternal + }; + project.Add(result); + return result; + } + } +} diff --git a/Confuser.MSBuild.Tasks/MSBuildLogger.cs b/Confuser.MSBuild.Tasks/MSBuildLogger.cs new file mode 100644 index 0000000..c0f81ed --- /dev/null +++ b/Confuser.MSBuild.Tasks/MSBuildLogger.cs @@ -0,0 +1,61 @@ +using System; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using ILogger = Confuser.Core.ILogger; + +namespace Confuser.MSBuild.Tasks { + internal sealed class MSBuildLogger : ILogger { + private readonly TaskLoggingHelper loggingHelper; + + internal bool HasError { get; private set; } + + internal MSBuildLogger(TaskLoggingHelper loggingHelper) => + this.loggingHelper = loggingHelper ?? throw new ArgumentNullException(nameof(loggingHelper)); + + void ILogger.Debug(string msg) => loggingHelper.LogMessage(MessageImportance.Low, "[DEBUG] " + msg); + + void ILogger.DebugFormat(string format, params object[] args) { + loggingHelper.LogMessage(MessageImportance.Low, "[DEBUG] " + format, args); + } + + void ILogger.EndProgress() {} + + void ILogger.Error(string msg) { + loggingHelper.LogError(msg); + HasError = true; + } + + void ILogger.ErrorException(string msg, Exception ex) { + loggingHelper.LogError(msg); + loggingHelper.LogErrorFromException(ex); + HasError = true; + } + + void ILogger.ErrorFormat(string format, params object[] args) { + loggingHelper.LogError(format, args); + HasError = true; + } + + void ILogger.Finish(bool successful) { + if (!successful) { + HasError = false; + } + } + + void ILogger.Info(string msg) => loggingHelper.LogMessage(MessageImportance.Normal, msg); + + void ILogger.InfoFormat(string format, params object[] args) => + loggingHelper.LogMessage(MessageImportance.Normal, format, args); + + void ILogger.Progress(int progress, int overall) { } + + void ILogger.Warn(string msg) => loggingHelper.LogWarning(msg); + + void ILogger.WarnException(string msg, Exception ex) { + loggingHelper.LogWarning(msg); + loggingHelper.LogWarningFromException(ex); + } + + void ILogger.WarnFormat(string format, params object[] args) => loggingHelper.LogWarning(format, args); + } +} diff --git a/Confuser.MSBuild.Tasks/build/Confuser.MSBuild.props b/Confuser.MSBuild.Tasks/build/Confuser.MSBuild.props new file mode 100644 index 0000000..4b4ed1c --- /dev/null +++ b/Confuser.MSBuild.Tasks/build/Confuser.MSBuild.props @@ -0,0 +1,19 @@ + + + + + + $(MSBuildThisFileDirectory)\..\netframework\ + + + + + $(MSBuildThisFileDirectory)\..\netstandard\ + + + + + + diff --git a/Confuser.MSBuild.Tasks/build/Confuser.MSBuild.targets b/Confuser.MSBuild.Tasks/build/Confuser.MSBuild.targets new file mode 100644 index 0000000..94d378c --- /dev/null +++ b/Confuser.MSBuild.Tasks/build/Confuser.MSBuild.targets @@ -0,0 +1,156 @@ + + + + $(AssemblyOriginatorKeyFile) + $(IntermediateOutputPath)confused\ + false + $(OutDir)confused\ + + + + $(MSBuildProjectDirectory)\$(MSBuildProjectName).crproj + + + + + CreateConfuserProject; + ConfuseAssembly; + _ReplaceOutputWithConfusedAssemblies; + _ReplaceDebugOutputWithConfusedAssemblies; + CopyConfusedFilesToOutputDirectory; + + + + + + + + + ResolveReferences; + ComputeIntermediateSatelliteAssemblies + + + + + + + + + + CreateConfuserProject + + + + + + + + + + + + + + + + + + + + + + + + + <_DebugSymbolsIntermediatePath Remove="@(_DebugSymbolsIntermediatePath)" /> + + + + + + + + + + false + false + + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs b/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs index d911be1..fe77a73 100644 --- a/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs +++ b/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs @@ -31,7 +31,7 @@ namespace Confuser.Renamer.Analyzers { if (!match.Success) continue; string typeName = match.Groups[1].Value; - TypeDef type = mainModule.FindReflectionThrow(typeName); + TypeDef type = mainModule.FindReflection(typeName); if (type == null) { context.Logger.WarnFormat("Could not find resource type '{0}'.", typeName); continue; diff --git a/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs b/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs index 4c28eeb..2afd098 100644 --- a/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs +++ b/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs @@ -98,6 +98,8 @@ namespace Confuser.Renamer.Analyzers { } void AnalyzeCAArgument(ConfuserContext context, INameService service, CAArgument arg) { + if (arg.Value == null) return; // null was passed to the custom attribute. We'll ignore that. + if (arg.Type.DefinitionAssembly.IsCorLib() && arg.Type.FullName == "System.Type") { var typeSig = (TypeSig)arg.Value; foreach (ITypeDefOrRef typeRef in typeSig.FindTypeRefs()) { diff --git a/Confuser2.sln b/Confuser2.sln index df66954..8b552fd 100644 --- a/Confuser2.sln +++ b/Confuser2.sln @@ -40,7 +40,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Confuser.UnitTest", "Tests\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntiTamper", "Tests\AntiTamper\AntiTamper.csproj", "{6A2BA6F7-3399-4890-9453-2D5BE8EEBBA9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AntiTamper.Test", "Tests\AntiTamper.Test\AntiTamper.Test.csproj", "{3F5558BD-7B94-4CB0-A46C-A7252B5BCA17}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntiTamper.Test", "Tests\AntiTamper.Test\AntiTamper.Test.csproj", "{3F5558BD-7B94-4CB0-A46C-A7252B5BCA17}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Confuser.MSBuild.Tasks", "Confuser.MSBuild.Tasks\Confuser.MSBuild.Tasks.csproj", "{91B12706-DC6A-45DE-97F1-FAF0901FF6AF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -100,6 +102,10 @@ Global {3F5558BD-7B94-4CB0-A46C-A7252B5BCA17}.Debug|Any CPU.Build.0 = Debug|Any CPU {3F5558BD-7B94-4CB0-A46C-A7252B5BCA17}.Release|Any CPU.ActiveCfg = Release|Any CPU {3F5558BD-7B94-4CB0-A46C-A7252B5BCA17}.Release|Any CPU.Build.0 = Release|Any CPU + {91B12706-DC6A-45DE-97F1-FAF0901FF6AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91B12706-DC6A-45DE-97F1-FAF0901FF6AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91B12706-DC6A-45DE-97F1-FAF0901FF6AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91B12706-DC6A-45DE-97F1-FAF0901FF6AF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Tests/CompressorWithResx.Test/CompressTest.cs b/Tests/CompressorWithResx.Test/CompressTest.cs index d656cfb..b5b87d3 100644 --- a/Tests/CompressorWithResx.Test/CompressTest.cs +++ b/Tests/CompressorWithResx.Test/CompressTest.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading.Tasks; @@ -16,16 +17,14 @@ namespace CompressorWithResx.Test { this.outputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper)); [Theory] - [InlineData("false", "normal")] - [InlineData("true", "normal")] - [InlineData("false", "dynamic")] - [InlineData("true", "dynamic")] + [MemberData(nameof(CompressAndExecuteTestData))] [Trait("Category", "Packer")] [Trait("Packer", "compressor")] - public async Task CompressAndExecuteTest(string compatKey, string deriverKey) { + public async Task CompressAndExecuteTest(string compatKey, string deriverKey, string resourceProtectionMode) { var baseDir = Environment.CurrentDirectory; var outputDir = Path.Combine(baseDir, "testtmp"); var inputFile = Path.Combine(baseDir, "CompressorWithResx.exe"); + var inputSatelliteFile = Path.Combine(baseDir, "de", "CompressorWithResx.resources.dll"); var outputFile = Path.Combine(outputDir, "CompressorWithResx.exe"); FileUtilities.ClearOutput(outputFile); var proj = new ConfuserProject { @@ -36,7 +35,17 @@ namespace CompressorWithResx.Test { { "key", deriverKey } } }; + + if (resourceProtectionMode != "none") { + proj.Rules.Add(new Rule() { + new SettingItem("resources") { + { "mode", resourceProtectionMode } + } + }); + } + proj.Add(new ProjectModule() { Path = inputFile }); + proj.Add(new ProjectModule() { Path = inputSatelliteFile }); var parameters = new ConfuserParameters { @@ -56,7 +65,8 @@ namespace CompressorWithResx.Test { using (var process = Process.Start(info)) { var stdout = process.StandardOutput; Assert.Equal("START", await stdout.ReadLineAsync()); - Assert.Equal("Test", await stdout.ReadLineAsync()); + Assert.Equal("Test (fallback)", await stdout.ReadLineAsync()); + Assert.Equal("Test (deutsch)", await stdout.ReadLineAsync()); Assert.Equal("END", await stdout.ReadLineAsync()); Assert.Empty(await stdout.ReadToEndAsync()); Assert.True(process.HasExited); @@ -65,5 +75,12 @@ namespace CompressorWithResx.Test { FileUtilities.ClearOutput(outputFile); } + + public static IEnumerable CompressAndExecuteTestData() { + foreach (var compressorCompatKey in new string[] { "true", "false" }) + foreach (var compressorDeriveKey in new string[] { "normal", "dynamic" }) + foreach (var resourceProtectionMode in new string[] { "none", "normal", "dynamic" }) + yield return new object[] { compressorCompatKey, compressorDeriveKey, resourceProtectionMode }; + } } } diff --git a/Tests/CompressorWithResx/Program.cs b/Tests/CompressorWithResx/Program.cs index e9b59f2..e79015a 100644 --- a/Tests/CompressorWithResx/Program.cs +++ b/Tests/CompressorWithResx/Program.cs @@ -4,6 +4,9 @@ namespace CompressorWithResx { public class Program { internal static int Main(string[] args) { Console.WriteLine("START"); + Properties.Resources.Culture = new System.Globalization.CultureInfo("en-US"); + Console.WriteLine(Properties.Resources.TestString); + Properties.Resources.Culture = new System.Globalization.CultureInfo("de-DE"); Console.WriteLine(Properties.Resources.TestString); Console.WriteLine("END"); return 42; diff --git a/Tests/CompressorWithResx/Properties/Resources.Designer.cs b/Tests/CompressorWithResx/Properties/Resources.Designer.cs index 7ce6fb4..cc1b77f 100644 --- a/Tests/CompressorWithResx/Properties/Resources.Designer.cs +++ b/Tests/CompressorWithResx/Properties/Resources.Designer.cs @@ -61,7 +61,7 @@ namespace CompressorWithResx.Properties { } /// - /// Sucht eine lokalisierte Zeichenfolge, die Test ähnelt. + /// Sucht eine lokalisierte Zeichenfolge, die Test (fallback) ähnelt. /// internal static string TestString { get { diff --git a/Tests/CompressorWithResx/Properties/Resources.de.resx b/Tests/CompressorWithResx/Properties/Resources.de.resx new file mode 100644 index 0000000..ed8fa15 --- /dev/null +++ b/Tests/CompressorWithResx/Properties/Resources.de.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Test (deutsch) + + \ No newline at end of file diff --git a/Tests/CompressorWithResx/Properties/Resources.resx b/Tests/CompressorWithResx/Properties/Resources.resx index 49d2049..f288128 100644 --- a/Tests/CompressorWithResx/Properties/Resources.resx +++ b/Tests/CompressorWithResx/Properties/Resources.resx @@ -118,6 +118,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Test + Test (fallback) \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 8fc91fc..a4c81db 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,3 +24,7 @@ build: artifacts: - path: Release/bin name: ConfuserEx + type: zip + +- path: Confuser.MSBuild.Tasks/bin/Release/*.nupkg + name: ConfuserEx.MSBuild