/// <summary> /// Validates the project file after it has been loaded from an INI file. /// </summary> public void ValidateProject() { // Validate stub ValidateStubIcon(); ValidateStubPadding(); ValidateManifest(); // Validate sources ValidateSources(); ValidateCompression(); ValidateEofData(); // Validate actions ValidateActions(); ValidateRunPE(); ValidateInvoke(); ValidateDrop(); ValidateMessageBox(); void ValidateStubIcon() { // Validate stub icon if (Project.Stub.IconPath != null) { // Validate that stub icon has .ico or .exe extension string extension = Path.GetExtension(Project.Stub.IconPath); if (extension.Equals(".ico", StringComparison.OrdinalIgnoreCase)) { // Validate that stub icon is a valid .ico file if (!IconExtractor.HasIcon(Project.Stub.IconPath)) { Errors.Add(ErrorSource.Project, ErrorSeverity.Error, "File '" + Path.GetFileName(Project.Stub.IconPath) + "' is not a valid icon."); } } else if (extension.Equals(".exe", StringComparison.OrdinalIgnoreCase)) { // Validate that stub icon is an executable file with an icon if (!IconExtractor.HasIcon(Project.Stub.IconPath)) { Errors.Add(ErrorSource.Project, ErrorSeverity.Error, "Could not extract icon from '" + Path.GetFileName(Project.Stub.IconPath) + "'."); } } else { Errors.Add(ErrorSource.Project, ErrorSeverity.Error, "Stub icon must have '.ico' or '.exe' extension."); } } } void ValidateStubPadding() { // Validate that stub has padding if (Project.Stub.Padding < 50) { Errors.Add(ErrorSource.Project, ErrorSeverity.Warning, "Padding is less than 50. The compiled binary will be of high-entropy and may be detected as a packer."); } } void ValidateManifest() { // Validate that manifest is not set to both template and cutom file if (Project.Manifest.Template != null && Project.Manifest.Path != null) { Errors.Add(ErrorSource.Project, ErrorSeverity.Error, "Both manifest template and path specified. Please specify only one."); } } void ValidateSources() { // Validate duplicate source ID's for (int i = 0; i < Project.Sources.Count; i++) { if (Project.Sources.Skip(i + 1).Any(source => source.Id == Project.Sources[i].Id)) { Errors.Add(ErrorSource.Project, ErrorSeverity.Error, "Duplicate source ID '" + Project.Sources[i].Id + "'."); } } // Exclude unused sources foreach (ProjectSource source in Project.Sources.ToArray()) { if (Project.Actions.None(action => action.Source == source)) { Project.RemoveSource(source); Errors.Add(ErrorSource.Project, ErrorSeverity.Warning, "Source '" + source.Id + "' was excluded, because it is not used."); } } } void ValidateCompression() { // Validate that compressed files are smaller than 100 MB foreach (EmbeddedSource source in Project.Sources.OfType <EmbeddedSource>()) { if (source.Compress && source.Path != null && new FileInfo(source.Path).Length > 1024 * 1024 * 100) { Errors.Add(ErrorSource.Project, ErrorSeverity.Warning, "File '" + Path.GetFileName(source.Path) + "' is bigger than 100 MB. It is recommended to disable compression."); } } } void ValidateEofData() { IEnumerable <EmbeddedSource> eofSources = Project.Sources.OfType <EmbeddedSource>().Where(source => source.EofData); // Validate that only PE files are used as EOF data sources foreach (EmbeddedSource source in eofSources) { if (new[] { ".exe", ".dll" }.None(extension => Path.GetExtension(source.Path).Equals(extension, StringComparison.OrdinalIgnoreCase))) { Errors.Add(ErrorSource.Project, ErrorSeverity.Error, "Cannot write EOF data of file '" + Path.GetFileName(source.Path) + "'. Only executable files contain EOF data."); } } // Validate that only one file's EOF data is written foreach (EmbeddedSource source in eofSources.Skip(1)) { Errors.Add(ErrorSource.Project, ErrorSeverity.Error, "Cannot write EOF data of file '" + Path.GetFileName(source.Path) + "'. Only one file's EOF data can be written."); } } void ValidateActions() { // Validate that at least one action exists if (Project.Actions.None()) { Errors.Add(ErrorSource.Project, ErrorSeverity.Warning, "The project is empty."); } } void ValidateRunPE() { // Validate that RunPE files have an .exe extension foreach (RunPEAction action in Project.Actions.OfType <RunPEAction>()) { if (action.Source is EmbeddedSource embeddedSource && embeddedSource.Path != null && !Path.GetExtension(embeddedSource.Path).Equals(".exe", StringComparison.OrdinalIgnoreCase)) { Errors.Add(ErrorSource.Project, ErrorSeverity.Error, "RunPE file must have '.exe' extension."); } } } void ValidateInvoke() { // Validate that invoke files have an .exe extension foreach (InvokeAction action in Project.Actions.OfType <InvokeAction>()) { if (action.Source is EmbeddedSource embeddedSource && embeddedSource.Path != null && !Path.GetExtension(embeddedSource.Path).Equals(".exe", StringComparison.OrdinalIgnoreCase)) { Errors.Add(ErrorSource.Project, ErrorSeverity.Error, "Invoked file must have '.exe' extension."); } } // Validate that invoke actions are only used in a .NET stub if (CSharp.EqualsNone(Project.Stub.Type, StubType.DotNet32, StubType.DotNet64) && Project.Actions.OfType <InvokeAction>().Any()) { Errors.Add(ErrorSource.Project, ErrorSeverity.Error, ".NET invocation is only supported in a .NET stub."); } } void ValidateDrop() { // Validate that a UAC manifest is included, if files are dropped in privileged directories IEnumerable <string> privilegedDropFileNames = Project.Actions .OfType <DropAction>() .Where(action => CSharp.EqualsAny(action.Location, DropLocation.WindowsDirectory, DropLocation.SystemDirectory, DropLocation.ProgramFiles, DropLocation.CDrive)) .Select(action => { if (action.FileName != null) { return(action.FileName); } else if (action.Source is EmbeddedSource embeddedSource && embeddedSource.Path != null) { return(Path.GetFileName(embeddedSource.Path)); }