/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System.Text; using System.Windows.Input; using dnlib.DotNet; using dnlib.DotNet.MD; using dnlib.DotNet.Writer; using dnlib.PE; using dnlib.W32Resources; using dnSpy.AsmEditor.Properties; using dnSpy.Contracts.Documents; using dnSpy.Contracts.MVVM; namespace dnSpy.AsmEditor.SaveModule { sealed class SaveModuleOptionsVM : SaveOptionsVM { public override SaveOptionsType Type => SaveOptionsType.Module; public ModuleDef Module { get; } public override object UndoDocument => document; readonly IDsDocument document; public ICommand ReinitializeCommand => new RelayCommand(a => Reinitialize()); public bool CanSaveMixedModeModule => Module is ModuleDefMD; public bool IsMixedModeModule => CanSaveMixedModeModule && !Module.IsILOnly; public bool CanPreserveOtherMetadataStreams => Module is ModuleDefMD; public bool UseMixedMode { get => useMixedMode; set { if (useMixedMode != value) { useMixedMode = value; OnPropertyChanged(nameof(UseMixedMode)); ReinitializeModuleWriterOptions(); } } } bool useMixedMode; public bool CanWritePdb => Module.PdbState is not null; public bool WritePdb { get => writePdb; set { if (writePdb != value) { writePdb = value; OnPropertyChanged(nameof(WritePdb)); } } } bool writePdb; public bool ShareMethodBodies { get => shareMethodBodies; set { if (value != shareMethodBodies) { shareMethodBodies = value; OnPropertyChanged(nameof(ShareMethodBodies)); } } } bool shareMethodBodies; public bool AddCheckSum { get => addCheckSum; set { if (value != addCheckSum) { addCheckSum = value; OnPropertyChanged(nameof(AddCheckSum)); } } } bool addCheckSum; public Win32Resources? Win32Resources { get => win32Resources; set { if (win32Resources != value) { win32Resources = value; OnPropertyChanged(nameof(Win32Resources)); } } } Win32Resources? win32Resources; public bool KeepExtraPEData { get => keepExtraPEData; set { if (keepExtraPEData != value) { keepExtraPEData = value; OnPropertyChanged(nameof(KeepExtraPEData)); } } } bool keepExtraPEData; public bool KeepWin32Resources { get => keepWin32Resources; set { if (keepWin32Resources != value) { keepWin32Resources = value; OnPropertyChanged(nameof(KeepWin32Resources)); } } } bool keepWin32Resources; internal static readonly EnumVM[] moduleKindList = EnumVM.Create(typeof(ModuleKind)); public EnumListVM ModuleKind { get; } public string Extension { get { switch ((ModuleKind)ModuleKind.SelectedItem!) { case dnlib.DotNet.ModuleKind.Console: case dnlib.DotNet.ModuleKind.Windows: default: return "exe"; case dnlib.DotNet.ModuleKind.Dll: return "dll"; case dnlib.DotNet.ModuleKind.NetModule: return "netmodule"; } } } public PEHeadersOptionsVM PEHeadersOptions { get; } public Cor20HeaderOptionsVM Cor20HeaderOptions { get; } public MetadataOptionsVM MetadataOptions { get; } public SaveModuleOptionsVM(IDsDocument document) { this.document = document; Module = document.ModuleDef!; PEHeadersOptions = new PEHeadersOptionsVM(Module.Machine, GetSubsystem(Module.Kind)); Cor20HeaderOptions = new Cor20HeaderOptionsVM(); MetadataOptions = new MetadataOptionsVM(Module); PEHeadersOptions.PropertyChanged += (s, e) => HasErrorUpdated(); Cor20HeaderOptions.PropertyChanged += (s, e) => HasErrorUpdated(); MetadataOptions.PropertyChanged += (s, e) => HasErrorUpdated(); ModuleKind = new EnumListVM(moduleKindList, (a, b) => { OnPropertyChanged(nameof(Extension)); PEHeadersOptions.Subsystem.SelectedItem = GetSubsystem((ModuleKind)ModuleKind!.SelectedItem!); PEHeadersOptions.Characteristics = CharacteristicsHelper.GetCharacteristics(PEHeadersOptions.Characteristics ?? 0, (ModuleKind)ModuleKind.SelectedItem!); }); Reinitialize(); } static Subsystem GetSubsystem(ModuleKind moduleKind) { if (moduleKind == dnlib.DotNet.ModuleKind.Windows) return Subsystem.WindowsGui; return Subsystem.WindowsCui; } void Reinitialize() { FileName = Module.Location; OriginalFileName = Module.Location; if (UseMixedMode == IsMixedModeModule) ReinitializeModuleWriterOptions(); else UseMixedMode = IsMixedModeModule; } void ReinitializeModuleWriterOptions() { if (UseMixedMode) InitializeFrom(new NativeModuleWriterOptions((ModuleDefMD)Module, optimizeImageSize: true)); else InitializeFrom(new ModuleWriterOptions(Module)); WritePdb = CanWritePdb; } public ModuleWriterOptionsBase CreateWriterOptions() { if (UseMixedMode) { var options = new NativeModuleWriterOptions((ModuleDefMD)Module, optimizeImageSize: true); CopyTo(options); options.KeepExtraPEData = KeepExtraPEData; options.KeepWin32Resources = KeepWin32Resources; return AddOtherOptions(options); } else { var options = new ModuleWriterOptions(Module); CopyTo(options); if (Module.ManagedEntryPoint is not null || Module.NativeEntryPoint == 0) options.Cor20HeaderOptions.Flags &= ~ComImageFlags.NativeEntryPoint; return AddOtherOptions(options); } } ModuleWriterOptionsBase AddOtherOptions(ModuleWriterOptionsBase options) { if (MetadataOptions.PreserveOtherMetadataStreams) options.MetadataOptions.PreserveHeapOrder(Module, addCustomHeaps: true); return options; } void CopyTo(ModuleWriterOptionsBase options) { PEHeadersOptions.CopyTo(options.PEHeadersOptions); Cor20HeaderOptions.CopyTo(options.Cor20HeaderOptions); MetadataOptions.CopyTo(options.MetadataOptions); options.WritePdb = WritePdb; options.ShareMethodBodies = ShareMethodBodies; options.AddCheckSum = AddCheckSum; options.Win32Resources = Win32Resources; options.ModuleKind = (ModuleKind)ModuleKind.SelectedItem!; } public SaveModuleOptionsVM Clone() => CopyTo(new SaveModuleOptionsVM(document)); public SaveModuleOptionsVM CopyTo(SaveModuleOptionsVM other) { other.FileName = FileName; other.OriginalFileName = OriginalFileName; other.UseMixedMode = UseMixedMode; other.MetadataOptions.PreserveOtherMetadataStreams = MetadataOptions.PreserveOtherMetadataStreams; other.InitializeFrom(CreateWriterOptions()); return other; } public void InitializeFrom(ModuleWriterOptionsBase options) { if (options is ModuleWriterOptions) InitializeFrom((ModuleWriterOptions)options); else InitializeFrom((NativeModuleWriterOptions)options); } public void InitializeFrom(ModuleWriterOptions options) { InitializeFromInternal((ModuleWriterOptionsBase)options); KeepExtraPEData = false; KeepWin32Resources = false; } public void InitializeFrom(NativeModuleWriterOptions options) { InitializeFromInternal((ModuleWriterOptionsBase)options); KeepExtraPEData = options.KeepExtraPEData; KeepWin32Resources = options.KeepWin32Resources; } void InitializeFromInternal(ModuleWriterOptionsBase options) { // Writing to it triggers a write to Subsystem so write it first ModuleKind.SelectedItem = options.ModuleKind; PEHeadersOptions.InitializeFrom(options.PEHeadersOptions); Cor20HeaderOptions.InitializeFrom(options.Cor20HeaderOptions); MetadataOptions.InitializeFrom(options.MetadataOptions); WritePdb = options.WritePdb; ShareMethodBodies = options.ShareMethodBodies; AddCheckSum = options.AddCheckSum; Win32Resources = options.Win32Resources; // Writing to Machine and ModuleKind triggers code that updates Characteristics PEHeadersOptions.Characteristics = options.PEHeadersOptions.Characteristics; } protected override string GetExtension(string filename) => Extension; public override bool HasError { get { return base.HasError || PEHeadersOptions.HasError || Cor20HeaderOptions.HasError || MetadataOptions.HasError; } } } sealed class PEHeadersOptionsVM : ViewModelBase { readonly Machine defaultMachine; readonly Subsystem defaultSubsystem; public PEHeadersOptionsVM(Machine defaultMachine, Subsystem defaultSubsystem) { this.defaultMachine = defaultMachine; this.defaultSubsystem = defaultSubsystem; Machine = new EnumListVM(machineList, (a, b) => { Characteristics = CharacteristicsHelper.GetCharacteristics(Characteristics ?? 0, (dnlib.PE.Machine)Machine!.SelectedItem!); }); TimeDateStamp = new NullableUInt32VM(a => HasErrorUpdated()); PointerToSymbolTable = new NullableUInt32VM(a => HasErrorUpdated()); NumberOfSymbols = new NullableUInt32VM(a => HasErrorUpdated()); MajorLinkerVersion = new NullableByteVM(a => HasErrorUpdated()); MinorLinkerVersion = new NullableByteVM(a => HasErrorUpdated()); ImageBase = new NullableUInt64VM(a => HasErrorUpdated()); SectionAlignment = new NullableUInt32VM(a => HasErrorUpdated()); FileAlignment = new NullableUInt32VM(a => HasErrorUpdated()); MajorOperatingSystemVersion = new NullableUInt16VM(a => HasErrorUpdated()); MinorOperatingSystemVersion = new NullableUInt16VM(a => HasErrorUpdated()); MajorImageVersion = new NullableUInt16VM(a => HasErrorUpdated()); MinorImageVersion = new NullableUInt16VM(a => HasErrorUpdated()); MajorSubsystemVersion = new NullableUInt16VM(a => HasErrorUpdated()); MinorSubsystemVersion = new NullableUInt16VM(a => HasErrorUpdated()); Win32VersionValue = new NullableUInt32VM(a => HasErrorUpdated()); SizeOfStackReserve = new NullableUInt64VM(a => HasErrorUpdated()); SizeOfStackCommit = new NullableUInt64VM(a => HasErrorUpdated()); SizeOfHeapReserve = new NullableUInt64VM(a => HasErrorUpdated()); SizeOfHeapCommit = new NullableUInt64VM(a => HasErrorUpdated()); LoaderFlags = new NullableUInt32VM(a => HasErrorUpdated()); NumberOfRvaAndSizes = new NullableUInt32VM(a => HasErrorUpdated()); } internal static readonly EnumVM[] machineList = EnumVM.Create(typeof(dnlib.PE.Machine), dnlib.PE.Machine.I386, dnlib.PE.Machine.AMD64, dnlib.PE.Machine.IA64, dnlib.PE.Machine.ARMNT, dnlib.PE.Machine.ARM64); public EnumListVM Machine { get; } public NullableUInt32VM TimeDateStamp { get; } public NullableUInt32VM PointerToSymbolTable { get; } public NullableUInt32VM NumberOfSymbols { get; } public Characteristics? Characteristics { get => characteristics; set { if (characteristics != value) { characteristics = value; OnPropertyChanged(nameof(Characteristics)); OnPropertyChanged(nameof(RelocsStripped)); OnPropertyChanged(nameof(ExecutableImage)); OnPropertyChanged(nameof(LineNumsStripped)); OnPropertyChanged(nameof(LocalSymsStripped)); OnPropertyChanged(nameof(AggressiveWsTrim)); OnPropertyChanged(nameof(LargeAddressAware)); OnPropertyChanged(nameof(CharacteristicsReserved1)); OnPropertyChanged(nameof(BytesReversedLo)); OnPropertyChanged(nameof(Bit32Machine)); OnPropertyChanged(nameof(DebugStripped)); OnPropertyChanged(nameof(RemovableRunFromSwap)); OnPropertyChanged(nameof(NetRunFromSwap)); OnPropertyChanged(nameof(System)); OnPropertyChanged(nameof(Dll)); OnPropertyChanged(nameof(UpSystemOnly)); OnPropertyChanged(nameof(BytesReversedHi)); } } } Characteristics? characteristics; public bool? RelocsStripped { get => GetFlagValue(dnlib.PE.Characteristics.RelocsStripped); set => SetFlagValue(dnlib.PE.Characteristics.RelocsStripped, value); } public bool? ExecutableImage { get => GetFlagValue(dnlib.PE.Characteristics.ExecutableImage); set => SetFlagValue(dnlib.PE.Characteristics.ExecutableImage, value); } public bool? LineNumsStripped { get => GetFlagValue(dnlib.PE.Characteristics.LineNumsStripped); set => SetFlagValue(dnlib.PE.Characteristics.LineNumsStripped, value); } public bool? LocalSymsStripped { get => GetFlagValue(dnlib.PE.Characteristics.LocalSymsStripped); set => SetFlagValue(dnlib.PE.Characteristics.LocalSymsStripped, value); } public bool? AggressiveWsTrim { get => GetFlagValue(dnlib.PE.Characteristics.AggressiveWsTrim); set => SetFlagValue(dnlib.PE.Characteristics.AggressiveWsTrim, value); } public bool? LargeAddressAware { get => GetFlagValue(dnlib.PE.Characteristics.LargeAddressAware); set => SetFlagValue(dnlib.PE.Characteristics.LargeAddressAware, value); } public bool? CharacteristicsReserved1 { get => GetFlagValue(dnlib.PE.Characteristics.Reserved1); set => SetFlagValue(dnlib.PE.Characteristics.Reserved1, value); } public bool? BytesReversedLo { get => GetFlagValue(dnlib.PE.Characteristics.BytesReversedLo); set => SetFlagValue(dnlib.PE.Characteristics.BytesReversedLo, value); } public bool? Bit32Machine { get => GetFlagValue(dnlib.PE.Characteristics.Bit32Machine); set => SetFlagValue(dnlib.PE.Characteristics.Bit32Machine, value); } public bool? DebugStripped { get => GetFlagValue(dnlib.PE.Characteristics.DebugStripped); set => SetFlagValue(dnlib.PE.Characteristics.DebugStripped, value); } public bool? RemovableRunFromSwap { get => GetFlagValue(dnlib.PE.Characteristics.RemovableRunFromSwap); set => SetFlagValue(dnlib.PE.Characteristics.RemovableRunFromSwap, value); } public bool? NetRunFromSwap { get => GetFlagValue(dnlib.PE.Characteristics.NetRunFromSwap); set => SetFlagValue(dnlib.PE.Characteristics.NetRunFromSwap, value); } public bool? System { get => GetFlagValue(dnlib.PE.Characteristics.System); set => SetFlagValue(dnlib.PE.Characteristics.System, value); } public bool? Dll { get => GetFlagValue(dnlib.PE.Characteristics.Dll); set => SetFlagValue(dnlib.PE.Characteristics.Dll, value); } public bool? UpSystemOnly { get => GetFlagValue(dnlib.PE.Characteristics.UpSystemOnly); set => SetFlagValue(dnlib.PE.Characteristics.UpSystemOnly, value); } public bool? BytesReversedHi { get => GetFlagValue(dnlib.PE.Characteristics.BytesReversedHi); set => SetFlagValue(dnlib.PE.Characteristics.BytesReversedHi, value); } bool? GetFlagValue(Characteristics flag) => Characteristics is null ? (bool?)null : (Characteristics.Value & flag) != 0; void SetFlagValue(Characteristics flag, bool? value) { if (Characteristics is null) Characteristics = 0; if (value ?? false) Characteristics |= flag; else Characteristics &= ~flag; } public NullableByteVM MajorLinkerVersion { get; } public NullableByteVM MinorLinkerVersion { get; } public NullableUInt64VM ImageBase { get; } public NullableUInt32VM SectionAlignment { get; } public NullableUInt32VM FileAlignment { get; } public NullableUInt16VM MajorOperatingSystemVersion { get; } public NullableUInt16VM MinorOperatingSystemVersion { get; } public NullableUInt16VM MajorImageVersion { get; } public NullableUInt16VM MinorImageVersion { get; } public NullableUInt16VM MajorSubsystemVersion { get; } public NullableUInt16VM MinorSubsystemVersion { get; } public NullableUInt32VM Win32VersionValue { get; } static readonly EnumVM[] subsystemList = EnumVM.Create(typeof(dnlib.PE.Subsystem), dnlib.PE.Subsystem.WindowsGui, dnlib.PE.Subsystem.WindowsCui); public EnumListVM Subsystem { get; } = new EnumListVM(subsystemList); public DllCharacteristics? DllCharacteristics { get => dllCharacteristics; set { if (dllCharacteristics != value) { dllCharacteristics = value; OnPropertyChanged(nameof(DllCharacteristics)); OnPropertyChanged(nameof(DllCharacteristicsReserved1)); OnPropertyChanged(nameof(DllCharacteristicsReserved2)); OnPropertyChanged(nameof(DllCharacteristicsReserved3)); OnPropertyChanged(nameof(DllCharacteristicsReserved4)); OnPropertyChanged(nameof(DllCharacteristicsReserved5)); OnPropertyChanged(nameof(HighEntropyVA)); OnPropertyChanged(nameof(DynamicBase)); OnPropertyChanged(nameof(ForceIntegrity)); OnPropertyChanged(nameof(NxCompat)); OnPropertyChanged(nameof(NoIsolation)); OnPropertyChanged(nameof(NoSeh)); OnPropertyChanged(nameof(NoBind)); OnPropertyChanged(nameof(AppContainer)); OnPropertyChanged(nameof(WdmDriver)); OnPropertyChanged(nameof(GuardCf)); OnPropertyChanged(nameof(TerminalServerAware)); } } } DllCharacteristics? dllCharacteristics; public bool? DllCharacteristicsReserved1 { get => GetFlagValue(dnlib.PE.DllCharacteristics.Reserved1); set => SetFlagValue(dnlib.PE.DllCharacteristics.Reserved1, value); } public bool? DllCharacteristicsReserved2 { get => GetFlagValue(dnlib.PE.DllCharacteristics.Reserved2); set => SetFlagValue(dnlib.PE.DllCharacteristics.Reserved2, value); } public bool? DllCharacteristicsReserved3 { get => GetFlagValue(dnlib.PE.DllCharacteristics.Reserved3); set => SetFlagValue(dnlib.PE.DllCharacteristics.Reserved3, value); } public bool? DllCharacteristicsReserved4 { get => GetFlagValue(dnlib.PE.DllCharacteristics.Reserved4); set => SetFlagValue(dnlib.PE.DllCharacteristics.Reserved4, value); } public bool? DllCharacteristicsReserved5 { get => GetFlagValue(dnlib.PE.DllCharacteristics.Reserved5); set => SetFlagValue(dnlib.PE.DllCharacteristics.Reserved5, value); } public bool? HighEntropyVA { get => GetFlagValue(dnlib.PE.DllCharacteristics.HighEntropyVA); set => SetFlagValue(dnlib.PE.DllCharacteristics.HighEntropyVA, value); } public bool? DynamicBase { get => GetFlagValue(dnlib.PE.DllCharacteristics.DynamicBase); set => SetFlagValue(dnlib.PE.DllCharacteristics.DynamicBase, value); } public bool? ForceIntegrity { get => GetFlagValue(dnlib.PE.DllCharacteristics.ForceIntegrity); set => SetFlagValue(dnlib.PE.DllCharacteristics.ForceIntegrity, value); } public bool? NxCompat { get => GetFlagValue(dnlib.PE.DllCharacteristics.NxCompat); set => SetFlagValue(dnlib.PE.DllCharacteristics.NxCompat, value); } public bool? NoIsolation { get => GetFlagValue(dnlib.PE.DllCharacteristics.NoIsolation); set => SetFlagValue(dnlib.PE.DllCharacteristics.NoIsolation, value); } public bool? NoSeh { get => GetFlagValue(dnlib.PE.DllCharacteristics.NoSeh); set => SetFlagValue(dnlib.PE.DllCharacteristics.NoSeh, value); } public bool? NoBind { get => GetFlagValue(dnlib.PE.DllCharacteristics.NoBind); set => SetFlagValue(dnlib.PE.DllCharacteristics.NoBind, value); } public bool? AppContainer { get => GetFlagValue(dnlib.PE.DllCharacteristics.AppContainer); set => SetFlagValue(dnlib.PE.DllCharacteristics.AppContainer, value); } public bool? WdmDriver { get => GetFlagValue(dnlib.PE.DllCharacteristics.WdmDriver); set => SetFlagValue(dnlib.PE.DllCharacteristics.WdmDriver, value); } public bool? GuardCf { get => GetFlagValue(dnlib.PE.DllCharacteristics.GuardCf); set => SetFlagValue(dnlib.PE.DllCharacteristics.GuardCf, value); } public bool? TerminalServerAware { get => GetFlagValue(dnlib.PE.DllCharacteristics.TerminalServerAware); set => SetFlagValue(dnlib.PE.DllCharacteristics.TerminalServerAware, value); } bool? GetFlagValue(DllCharacteristics flag) => DllCharacteristics is null ? (bool?)null : (DllCharacteristics.Value & flag) != 0; void SetFlagValue(DllCharacteristics flag, bool? value) { if (DllCharacteristics is null) DllCharacteristics = 0; if (value ?? false) DllCharacteristics |= flag; else DllCharacteristics &= ~flag; } public NullableUInt64VM SizeOfStackReserve { get; } public NullableUInt64VM SizeOfStackCommit { get; } public NullableUInt64VM SizeOfHeapReserve { get; } public NullableUInt64VM SizeOfHeapCommit { get; } public NullableUInt32VM LoaderFlags { get; } public NullableUInt32VM NumberOfRvaAndSizes { get; } public void CopyTo(PEHeadersOptions options) { options.Machine = (dnlib.PE.Machine)Machine.SelectedItem!; options.TimeDateStamp = TimeDateStamp.Value; options.PointerToSymbolTable = PointerToSymbolTable.Value; options.NumberOfSymbols = NumberOfSymbols.Value; options.Characteristics = Characteristics; options.MajorLinkerVersion = MajorLinkerVersion.Value; options.MinorLinkerVersion = MinorLinkerVersion.Value; options.ImageBase = ImageBase.Value; options.SectionAlignment = SectionAlignment.Value; options.FileAlignment = FileAlignment.Value; options.MajorOperatingSystemVersion = MajorOperatingSystemVersion.Value; options.MinorOperatingSystemVersion = MinorOperatingSystemVersion.Value; options.MajorImageVersion = MajorImageVersion.Value; options.MinorImageVersion = MinorImageVersion.Value; options.MajorSubsystemVersion = MajorSubsystemVersion.Value; options.MinorSubsystemVersion = MinorSubsystemVersion.Value; options.Win32VersionValue = Win32VersionValue.Value; options.Subsystem = (dnlib.PE.Subsystem)Subsystem.SelectedItem!; options.DllCharacteristics = DllCharacteristics; options.SizeOfStackReserve = SizeOfStackReserve.Value; options.SizeOfStackCommit = SizeOfStackCommit.Value; options.SizeOfHeapReserve = SizeOfHeapReserve.Value; options.SizeOfHeapCommit = SizeOfHeapCommit.Value; options.LoaderFlags = LoaderFlags.Value; options.NumberOfRvaAndSizes = NumberOfRvaAndSizes.Value; } public void InitializeFrom(PEHeadersOptions options) { Machine.SelectedItem = options.Machine ?? defaultMachine; TimeDateStamp.Value = options.TimeDateStamp; PointerToSymbolTable.Value = options.PointerToSymbolTable; NumberOfSymbols.Value = options.NumberOfSymbols; Characteristics = options.Characteristics; MajorLinkerVersion.Value = options.MajorLinkerVersion; MinorLinkerVersion.Value = options.MinorLinkerVersion; ImageBase.Value = options.ImageBase; SectionAlignment.Value = options.SectionAlignment; FileAlignment.Value = options.FileAlignment; MajorOperatingSystemVersion.Value = options.MajorOperatingSystemVersion; MinorOperatingSystemVersion.Value = options.MinorOperatingSystemVersion; MajorImageVersion.Value = options.MajorImageVersion; MinorImageVersion.Value = options.MinorImageVersion; MajorSubsystemVersion.Value = options.MajorSubsystemVersion; MinorSubsystemVersion.Value = options.MinorSubsystemVersion; Win32VersionValue.Value = options.Win32VersionValue; Subsystem.SelectedItem = options.Subsystem ?? defaultSubsystem; DllCharacteristics = options.DllCharacteristics; SizeOfStackReserve.Value = options.SizeOfStackReserve; SizeOfStackCommit.Value = options.SizeOfStackCommit; SizeOfHeapReserve.Value = options.SizeOfHeapReserve; SizeOfHeapCommit.Value = options.SizeOfHeapCommit; LoaderFlags.Value = options.LoaderFlags; NumberOfRvaAndSizes.Value = options.NumberOfRvaAndSizes; } public override bool HasError { get { return TimeDateStamp.HasError || PointerToSymbolTable.HasError || NumberOfSymbols.HasError || MajorLinkerVersion.HasError || MinorLinkerVersion.HasError || ImageBase.HasError || SectionAlignment.HasError || FileAlignment.HasError || MajorOperatingSystemVersion.HasError || MinorOperatingSystemVersion.HasError || MajorImageVersion.HasError || MinorImageVersion.HasError || MajorSubsystemVersion.HasError || MinorSubsystemVersion.HasError || Win32VersionValue.HasError || SizeOfStackReserve.HasError || SizeOfStackCommit.HasError || SizeOfHeapReserve.HasError || SizeOfHeapCommit.HasError || LoaderFlags.HasError || NumberOfRvaAndSizes.HasError; } } } sealed class Cor20HeaderOptionsVM : ViewModelBase { public Cor20HeaderOptionsVM() { MajorRuntimeVersion = new NullableUInt16VM(a => HasErrorUpdated()); MinorRuntimeVersion = new NullableUInt16VM(a => HasErrorUpdated()); EntryPoint = new NullableUInt32VM(a => HasErrorUpdated()); } public NullableUInt16VM MajorRuntimeVersion { get; } public NullableUInt16VM MinorRuntimeVersion { get; } public ComImageFlags? Flags { get => flags; set { if (flags != value) { flags = value; OnPropertyChanged(nameof(Flags)); OnPropertyChanged(nameof(ILOnly)); OnPropertyChanged(nameof(Bit32Required)); OnPropertyChanged(nameof(ILLibrary)); OnPropertyChanged(nameof(StrongNameSigned)); OnPropertyChanged(nameof(TrackDebugData)); OnPropertyChanged(nameof(Bit32Preferred)); } } } ComImageFlags? flags; public bool? ILOnly { get => GetFlagValue(ComImageFlags.ILOnly); set => SetFlagValue(ComImageFlags.ILOnly, value); } public bool? Bit32Required { get => GetFlagValue(ComImageFlags.Bit32Required); set => SetFlagValue(ComImageFlags.Bit32Required, value); } public bool? ILLibrary { get => GetFlagValue(ComImageFlags.ILLibrary); set => SetFlagValue(ComImageFlags.ILLibrary, value); } public bool? StrongNameSigned { get => GetFlagValue(ComImageFlags.StrongNameSigned); set => SetFlagValue(ComImageFlags.StrongNameSigned, value); } public bool? TrackDebugData { get => GetFlagValue(ComImageFlags.TrackDebugData); set => SetFlagValue(ComImageFlags.TrackDebugData, value); } public bool? Bit32Preferred { get => GetFlagValue(ComImageFlags.Bit32Preferred); set => SetFlagValue(ComImageFlags.Bit32Preferred, value); } bool? GetFlagValue(ComImageFlags flag) => Flags is null ? (bool?)null : (Flags.Value & flag) != 0; void SetFlagValue(ComImageFlags flag, bool? value) { if (Flags is null) Flags = 0; if (value ?? false) Flags |= flag; else Flags &= ~flag; } public NullableUInt32VM EntryPoint { get; } public void CopyTo(Cor20HeaderOptions options) { options.MajorRuntimeVersion = MajorRuntimeVersion.Value; options.MinorRuntimeVersion = MinorRuntimeVersion.Value; options.Flags = Flags; options.EntryPoint = EntryPoint.Value; } public void InitializeFrom(Cor20HeaderOptions options) { MajorRuntimeVersion.Value = options.MajorRuntimeVersion; MinorRuntimeVersion.Value = options.MinorRuntimeVersion; Flags = options.Flags; EntryPoint.Value = options.EntryPoint; } public override bool HasError { get { return MajorRuntimeVersion.HasError || MinorRuntimeVersion.HasError || EntryPoint.HasError; } } } sealed class MetadataOptionsVM : ViewModelBase { readonly ModuleDef module; public MetadataOptionsVM(ModuleDef module) { this.module = module; MetadataHeaderOptions = new MetadataHeaderOptionsVM(); TablesHeapOptions = new TablesHeapOptionsVM(); MetadataHeaderOptions.PropertyChanged += (s, e) => HasErrorUpdated(); TablesHeapOptions.PropertyChanged += (s, e) => HasErrorUpdated(); } public MetadataHeaderOptionsVM MetadataHeaderOptions { get; } public TablesHeapOptionsVM TablesHeapOptions { get; } public MetadataFlags Flags { get => flags; set { if (flags != value) { flags = value; OnPropertyChanged(nameof(Flags)); } } } MetadataFlags flags; public bool PreserveTypeRefRids { get => GetFlagValue(MetadataFlags.PreserveTypeRefRids); set => SetFlagValue(MetadataFlags.PreserveTypeRefRids, value, nameof(PreserveRids), nameof(PreserveTypeRefRids)); } public bool PreserveTypeDefRids { get => GetFlagValue(MetadataFlags.PreserveTypeDefRids); set => SetFlagValue(MetadataFlags.PreserveTypeDefRids, value, nameof(PreserveRids), nameof(PreserveTypeDefRids)); } public bool PreserveFieldRids { get => GetFlagValue(MetadataFlags.PreserveFieldRids); set => SetFlagValue(MetadataFlags.PreserveFieldRids, value, nameof(PreserveRids), nameof(PreserveFieldRids)); } public bool PreserveMethodRids { get => GetFlagValue(MetadataFlags.PreserveMethodRids); set => SetFlagValue(MetadataFlags.PreserveMethodRids, value, nameof(PreserveRids), nameof(PreserveMethodRids)); } public bool PreserveParamRids { get => GetFlagValue(MetadataFlags.PreserveParamRids); set => SetFlagValue(MetadataFlags.PreserveParamRids, value, nameof(PreserveRids), nameof(PreserveParamRids)); } public bool PreserveMemberRefRids { get => GetFlagValue(MetadataFlags.PreserveMemberRefRids); set => SetFlagValue(MetadataFlags.PreserveMemberRefRids, value, nameof(PreserveRids), nameof(PreserveMemberRefRids)); } public bool PreserveStandAloneSigRids { get => GetFlagValue(MetadataFlags.PreserveStandAloneSigRids); set => SetFlagValue(MetadataFlags.PreserveStandAloneSigRids, value, nameof(PreserveRids), nameof(PreserveStandAloneSigRids)); } public bool PreserveEventRids { get => GetFlagValue(MetadataFlags.PreserveEventRids); set => SetFlagValue(MetadataFlags.PreserveEventRids, value, nameof(PreserveRids), nameof(PreserveEventRids)); } public bool PreservePropertyRids { get => GetFlagValue(MetadataFlags.PreservePropertyRids); set => SetFlagValue(MetadataFlags.PreservePropertyRids, value, nameof(PreserveRids), nameof(PreservePropertyRids)); } public bool PreserveTypeSpecRids { get => GetFlagValue(MetadataFlags.PreserveTypeSpecRids); set => SetFlagValue(MetadataFlags.PreserveTypeSpecRids, value, nameof(PreserveRids), nameof(PreserveTypeSpecRids)); } public bool PreserveMethodSpecRids { get => GetFlagValue(MetadataFlags.PreserveMethodSpecRids); set => SetFlagValue(MetadataFlags.PreserveMethodSpecRids, value, nameof(PreserveRids), nameof(PreserveMethodSpecRids)); } public bool? PreserveRids { get { var val = Flags & MetadataFlags.PreserveRids; if (val == MetadataFlags.PreserveRids) return true; if (val == 0) return false; return null; } set { if (value is not null && value != PreserveRids) { if (value.Value) Flags |= MetadataFlags.PreserveRids; else Flags &= ~MetadataFlags.PreserveRids; OnPropertyChanged(nameof(PreserveRids)); OnPropertyChanged(nameof(PreserveTypeRefRids)); OnPropertyChanged(nameof(PreserveTypeDefRids)); OnPropertyChanged(nameof(PreserveFieldRids)); OnPropertyChanged(nameof(PreserveMethodRids)); OnPropertyChanged(nameof(PreserveParamRids)); OnPropertyChanged(nameof(PreserveMemberRefRids)); OnPropertyChanged(nameof(PreserveStandAloneSigRids)); OnPropertyChanged(nameof(PreserveEventRids)); OnPropertyChanged(nameof(PreservePropertyRids)); OnPropertyChanged(nameof(PreserveTypeSpecRids)); OnPropertyChanged(nameof(PreserveMethodSpecRids)); } } } public bool PreserveStringsOffsets { get => GetFlagValue(MetadataFlags.PreserveStringsOffsets); set => SetFlagValue(MetadataFlags.PreserveStringsOffsets, value, nameof(PreserveStringsOffsets)); } public bool PreserveUSOffsets { get => GetFlagValue(MetadataFlags.PreserveUSOffsets); set => SetFlagValue(MetadataFlags.PreserveUSOffsets, value, nameof(PreserveUSOffsets)); } public bool PreserveBlobOffsets { get => GetFlagValue(MetadataFlags.PreserveBlobOffsets); set => SetFlagValue(MetadataFlags.PreserveBlobOffsets, value, nameof(PreserveBlobOffsets)); } public bool PreserveExtraSignatureData { get => GetFlagValue(MetadataFlags.PreserveExtraSignatureData); set => SetFlagValue(MetadataFlags.PreserveExtraSignatureData, value, nameof(PreserveExtraSignatureData)); } public bool KeepOldMaxStack { get => GetFlagValue(MetadataFlags.KeepOldMaxStack); set => SetFlagValue(MetadataFlags.KeepOldMaxStack, value, nameof(KeepOldMaxStack)); } public bool AlwaysCreateGuidHeap { get => GetFlagValue(MetadataFlags.AlwaysCreateGuidHeap); set => SetFlagValue(MetadataFlags.AlwaysCreateGuidHeap, value, nameof(AlwaysCreateGuidHeap)); } public bool AlwaysCreateStringsHeap { get => GetFlagValue(MetadataFlags.AlwaysCreateStringsHeap); set => SetFlagValue(MetadataFlags.AlwaysCreateStringsHeap, value, nameof(AlwaysCreateStringsHeap)); } public bool AlwaysCreateUSHeap { get => GetFlagValue(MetadataFlags.AlwaysCreateUSHeap); set => SetFlagValue(MetadataFlags.AlwaysCreateUSHeap, value, nameof(AlwaysCreateUSHeap)); } public bool AlwaysCreateBlobHeap { get => GetFlagValue(MetadataFlags.AlwaysCreateBlobHeap); set => SetFlagValue(MetadataFlags.AlwaysCreateBlobHeap, value, nameof(AlwaysCreateBlobHeap)); } public bool PreserveOtherMetadataStreams { get => preserveOtherMetadataStreams; set { if (preserveOtherMetadataStreams != value) { preserveOtherMetadataStreams = value; OnPropertyChanged(nameof(PreserveOtherMetadataStreams)); if (preserveOtherMetadataStreams && module is ModuleDefMD mod) { if (mod.Metadata.GuidStream.StartOffset != 0) AlwaysCreateGuidHeap = true; if (mod.Metadata.StringsStream.StartOffset != 0) AlwaysCreateStringsHeap = true; if (mod.Metadata.USStream.StartOffset != 0) AlwaysCreateUSHeap = true; if (mod.Metadata.BlobStream.StartOffset != 0) AlwaysCreateBlobHeap = true; } } } } bool preserveOtherMetadataStreams; bool GetFlagValue(MetadataFlags flag) => (Flags & flag) != 0; void SetFlagValue(MetadataFlags flag, bool value, string prop1, string? prop2 = null) { bool origValue = (Flags & flag) != 0; if (origValue == value) return; if (value) Flags |= flag; else Flags &= ~flag; OnPropertyChanged(prop1); if (prop2 is not null) OnPropertyChanged(prop2); } public void CopyTo(MetadataOptions options) { MetadataHeaderOptions.CopyTo(options.MetadataHeaderOptions); TablesHeapOptions.CopyTo(options.TablesHeapOptions); options.Flags = Flags; } public void InitializeFrom(MetadataOptions options) { MetadataHeaderOptions.InitializeFrom(options.MetadataHeaderOptions); TablesHeapOptions.InitializeFrom(options.TablesHeapOptions); Flags = options.Flags; PreserveOtherMetadataStreams = false; OnFlagsChanged(); } void OnFlagsChanged() { OnPropertyChanged(nameof(PreserveTypeRefRids)); OnPropertyChanged(nameof(PreserveTypeDefRids)); OnPropertyChanged(nameof(PreserveFieldRids)); OnPropertyChanged(nameof(PreserveMethodRids)); OnPropertyChanged(nameof(PreserveParamRids)); OnPropertyChanged(nameof(PreserveMemberRefRids)); OnPropertyChanged(nameof(PreserveStandAloneSigRids)); OnPropertyChanged(nameof(PreserveEventRids)); OnPropertyChanged(nameof(PreservePropertyRids)); OnPropertyChanged(nameof(PreserveTypeSpecRids)); OnPropertyChanged(nameof(PreserveMethodSpecRids)); OnPropertyChanged(nameof(PreserveRids)); OnPropertyChanged(nameof(PreserveStringsOffsets)); OnPropertyChanged(nameof(PreserveUSOffsets)); OnPropertyChanged(nameof(PreserveBlobOffsets)); OnPropertyChanged(nameof(PreserveExtraSignatureData)); OnPropertyChanged(nameof(KeepOldMaxStack)); OnPropertyChanged(nameof(AlwaysCreateGuidHeap)); OnPropertyChanged(nameof(AlwaysCreateStringsHeap)); OnPropertyChanged(nameof(AlwaysCreateUSHeap)); OnPropertyChanged(nameof(AlwaysCreateBlobHeap)); } public override bool HasError { get { return MetadataHeaderOptions.HasError || TablesHeapOptions.HasError; } } } sealed class MetadataHeaderOptionsVM : ViewModelBase { public MetadataHeaderOptionsVM() { Signature = new NullableUInt32VM(a => HasErrorUpdated()); MajorVersion = new NullableUInt16VM(a => HasErrorUpdated()); MinorVersion = new NullableUInt16VM(a => HasErrorUpdated()); Reserved1 = new NullableUInt32VM(a => HasErrorUpdated()); StorageFlags = new NullableByteVM(a => HasErrorUpdated()); Reserved2 = new NullableByteVM(a => HasErrorUpdated()); } public NullableUInt32VM Signature { get; } public NullableUInt16VM MajorVersion { get; } public NullableUInt16VM MinorVersion { get; } public NullableUInt32VM Reserved1 { get; } public string? VersionString { get => versionString; set { versionString = value; OnPropertyChanged(nameof(VersionString)); HasErrorUpdated(); } } string? versionString; public NullableByteVM StorageFlags { get; } public NullableByteVM Reserved2 { get; } public void CopyTo(MetadataHeaderOptions options) { options.Signature = Signature.Value; options.MajorVersion = MajorVersion.Value; options.MinorVersion = MinorVersion.Value; options.Reserved1 = Reserved1.Value; options.VersionString = string.IsNullOrEmpty(VersionString) ? null : VersionString; options.StorageFlags = (StorageFlags?)StorageFlags.Value; options.Reserved2 = Reserved2.Value; } public void InitializeFrom(MetadataHeaderOptions options) { Signature.Value = options.Signature; MajorVersion.Value = options.MajorVersion; MinorVersion.Value = options.MinorVersion; Reserved1.Value = options.Reserved1; VersionString = options.VersionString; StorageFlags.Value = (byte?)options.StorageFlags; Reserved2.Value = options.Reserved2; } protected override string? Verify(string columnName) { if (columnName == nameof(VersionString)) return ValidateVersionString(versionString); return string.Empty; } public override bool HasError { get { if (!string.IsNullOrEmpty(Verify(nameof(VersionString)))) return true; return Signature.HasError || MajorVersion.HasError || MinorVersion.HasError || Reserved1.HasError || StorageFlags.HasError || Reserved2.HasError; } } internal static string ValidateVersionString(string? versionString) { var bytes = Encoding.UTF8.GetBytes(versionString + "\0"); if (bytes.Length > 256) return dnSpy_AsmEditor_Resources.Error_VersionStringTooLong; return string.Empty; } } sealed class TablesHeapOptionsVM : ViewModelBase { public TablesHeapOptionsVM() { Reserved1 = new NullableUInt32VM(a => HasErrorUpdated()); MajorVersion = new NullableByteVM(a => HasErrorUpdated()); MinorVersion = new NullableByteVM(a => HasErrorUpdated()); ExtraData = new NullableUInt32VM(a => HasErrorUpdated()); } public NullableUInt32VM Reserved1 { get; } public NullableByteVM MajorVersion { get; } public NullableByteVM MinorVersion { get; } public bool? UseENC { get => useENC; set { useENC = value; OnPropertyChanged(nameof(UseENC)); } } bool? useENC; public NullableUInt32VM ExtraData { get; } public bool? HasDeletedRows { get => hasDeletedRows; set { hasDeletedRows = value; OnPropertyChanged(nameof(HasDeletedRows)); } } bool? hasDeletedRows; public void CopyTo(TablesHeapOptions options) { options.Reserved1 = Reserved1.Value; options.MajorVersion = MajorVersion.Value; options.MinorVersion = MinorVersion.Value; options.UseENC = UseENC; options.ExtraData = ExtraData.Value; options.HasDeletedRows = HasDeletedRows; } public void InitializeFrom(TablesHeapOptions options) { Reserved1.Value = options.Reserved1; MajorVersion.Value = options.MajorVersion; MinorVersion.Value = options.MinorVersion; UseENC = options.UseENC; ExtraData.Value = options.ExtraData; HasDeletedRows = options.HasDeletedRows; } public override bool HasError { get { return Reserved1.HasError || MajorVersion.HasError || MinorVersion.HasError || ExtraData.HasError; } } } }