Dnspy/Extensions/dnSpy.AsmEditor/Module/ModuleOptionsVM.cs
2021-09-20 18:20:01 +02:00

572 lines
19 KiB
C#

/*
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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Linq;
using System.Windows.Input;
using dnlib.DotNet;
using dnlib.DotNet.MD;
using dnlib.PE;
using dnSpy.AsmEditor.DnlibDialogs;
using dnSpy.AsmEditor.Properties;
using dnSpy.AsmEditor.ViewHelpers;
using dnSpy.Contracts.Decompiler;
using dnSpy.Contracts.MVVM;
using dnSpy.Contracts.Search;
namespace dnSpy.AsmEditor.Module {
enum EntryPointType {
None,
Managed,
Native,
}
sealed class ModuleOptionsVM : ViewModelBase {
readonly ModuleOptions options;
readonly ModuleOptions origOptions;
public IDnlibTypePicker DnlibTypePicker {
set => dnlibTypePicker = value;
}
IDnlibTypePicker? dnlibTypePicker;
public ICommand PickManagedEntryPointCommand => new RelayCommand(a => PickManagedEntryPoint());
public ICommand ReinitializeCommand => new RelayCommand(a => Reinitialize());
public ICommand GenerateNewMvidCommand => new RelayCommand(a => Mvid.Value = Guid.NewGuid());
public ICommand GenerateNewEncIdCommand => new RelayCommand(a => EncId.Value = Guid.NewGuid());
public ICommand GenerateNewEncBaseIdCommand => new RelayCommand(a => EncBaseId.Value = Guid.NewGuid());
public NullableGuidVM Mvid { get; }
public NullableGuidVM EncId { get; }
public NullableGuidVM EncBaseId { get; }
public string Name {
get => options.Name;
set {
options.Name = value;
OnPropertyChanged(nameof(Name));
}
}
public EnumListVM ClrVersion { get; }
public EnumListVM ModuleKind { get; }
public Characteristics Characteristics {
get => options.Characteristics;
set {
if (options.Characteristics != value) {
options.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));
}
}
}
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 & flag) != 0;
void SetFlagValue(Characteristics flag, bool value) {
if (value)
Characteristics |= flag;
else
Characteristics &= ~flag;
}
public DllCharacteristics DllCharacteristics {
get => options.DllCharacteristics;
set {
if (options.DllCharacteristics != value) {
options.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));
}
}
}
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 & flag) != 0;
void SetFlagValue(DllCharacteristics flag, bool value) {
if (value)
DllCharacteristics |= flag;
else
DllCharacteristics &= ~flag;
}
public string? RuntimeVersion {
get => options.RuntimeVersion;
set {
options.RuntimeVersion = value;
OnPropertyChanged(nameof(RuntimeVersion));
UpdateClrVersion();
HasErrorUpdated();
}
}
public EnumListVM Machine { get; }
public ComImageFlags Cor20HeaderFlags {
get => options.Cor20HeaderFlags;
set {
if (options.Cor20HeaderFlags != value) {
options.Cor20HeaderFlags = value;
OnPropertyChanged(nameof(Cor20HeaderFlags));
OnPropertyChanged(nameof(ILOnly));
OnPropertyChanged(nameof(Bit32Required));
OnPropertyChanged(nameof(ILLibrary));
OnPropertyChanged(nameof(StrongNameSigned));
OnPropertyChanged(nameof(TrackDebugData));
OnPropertyChanged(nameof(Bit32Preferred));
}
}
}
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) => (Cor20HeaderFlags & flag) != 0;
void SetFlagValue(ComImageFlags flag, bool value) {
if (value)
Cor20HeaderFlags |= flag;
else
Cor20HeaderFlags &= ~flag;
}
public NullableUInt32VM Cor20HeaderRuntimeVersion { get; }
public NullableUInt16VM TablesHeaderVersion { get; }
public EntryPointType EntryPointEnum {
get => entryPointEnum;
set {
if (entryPointEnum != value) {
entryPointEnum = value;
OnPropertyChanged(nameof(EntryPointEnum));
if (entryPointEnum == EntryPointType.Native)
Cor20HeaderFlags |= ComImageFlags.NativeEntryPoint;
else
Cor20HeaderFlags &= ~ComImageFlags.NativeEntryPoint;
}
}
}
EntryPointType entryPointEnum;
public IManagedEntryPoint? ManagedEntryPoint {
get => managedEntryPoint;
set {
if (managedEntryPoint != value) {
managedEntryPoint = value;
OnPropertyChanged(nameof(ManagedEntryPoint));
OnPropertyChanged(nameof(EntryPointName));
OnPropertyChanged(nameof(EntryPointNameToolTip));
}
}
}
IManagedEntryPoint? managedEntryPoint;
public string EntryPointName => GetEntryPointString(80);
public string? EntryPointNameToolTip => ManagedEntryPoint is null ? null : GetEntryPointString(500);
string GetEntryPointString(int maxChars) {
var ep = ManagedEntryPoint;
if (ep is null)
return string.Empty;
string s;
if (ep is MethodDef method) {
var declType = method.DeclaringType;
if (declType is not null)
s = $"{method.Name} ({declType.FullName})";
else
s = method.Name;
}
else {
//TODO: Support EP in other module
s = string.Empty;
}
if (s.Length > maxChars)
s = s.Substring(0, maxChars) + "...";
return s;
}
public UInt32VM NativeEntryPointRva { get; }
public CustomAttributesVM CustomAttributesVM { get; }
readonly ModuleDef module;
public ModuleOptionsVM(ModuleDef module, ModuleOptions options, IDecompilerService decompilerService) {
this.module = module;
this.options = new ModuleOptions();
origOptions = options;
ModuleKind = new EnumListVM(SaveModule.SaveModuleOptionsVM.moduleKindList, (a, b) => {
Characteristics = SaveModule.CharacteristicsHelper.GetCharacteristics(Characteristics, (dnlib.DotNet.ModuleKind)ModuleKind!.SelectedItem!);
});
Machine = new EnumListVM(SaveModule.PEHeadersOptionsVM.machineList, (a, b) => {
Characteristics = SaveModule.CharacteristicsHelper.GetCharacteristics(Characteristics, (dnlib.PE.Machine)Machine!.SelectedItem!);
});
Mvid = new NullableGuidVM(a => HasErrorUpdated());
EncId = new NullableGuidVM(a => HasErrorUpdated());
EncBaseId = new NullableGuidVM(a => HasErrorUpdated());
ClrVersion = new EnumListVM(NetModuleOptionsVM.clrVersionList, (a, b) => OnClrVersionChanged());
ClrVersion.Items.Add(new EnumVM(Module.ClrVersion.Unknown, dnSpy_AsmEditor_Resources.Unknown));
ClrVersion.SelectedItem = Module.ClrVersion.Unknown;
Cor20HeaderRuntimeVersion = new NullableUInt32VM(a => { HasErrorUpdated(); UpdateClrVersion(); });
TablesHeaderVersion = new NullableUInt16VM(a => { HasErrorUpdated(); UpdateClrVersion(); });
NativeEntryPointRva = new UInt32VM(a => HasErrorUpdated());
CustomAttributesVM = new CustomAttributesVM(module, decompilerService);
Reinitialize();
}
void OnClrVersionChanged() {
var clrVersion = (Module.ClrVersion)ClrVersion.SelectedItem!;
var clrValues = ClrVersionValues.GetValues(clrVersion);
if (clrValues is null)
return;
if (Cor20HeaderRuntimeVersion is not null)
Cor20HeaderRuntimeVersion.Value = clrValues.Cor20HeaderRuntimeVersion;
if (TablesHeaderVersion is not null)
TablesHeaderVersion.Value = clrValues.TablesHeaderVersion;
RuntimeVersion = clrValues.RuntimeVersion;
}
void UpdateClrVersion() {
ClrVersion clrVersion = Module.ClrVersion.Unknown;
if (Cor20HeaderRuntimeVersion is not null && !Cor20HeaderRuntimeVersion.HasError && Cor20HeaderRuntimeVersion.Value is not null &&
TablesHeaderVersion is not null && !TablesHeaderVersion.HasError && TablesHeaderVersion.Value is not null) {
var clrValues = ClrVersionValues.Find(Cor20HeaderRuntimeVersion.Value.Value, TablesHeaderVersion.Value.Value, RuntimeVersion);
if (clrValues is not null)
clrVersion = clrValues.ClrVersion;
}
if (ClrVersion is not null)
ClrVersion.SelectedItem = clrVersion;
}
void Reinitialize() => InitializeFrom(origOptions);
public ModuleOptions CreateModuleOptions() => CopyTo(new ModuleOptions());
void InitializeFrom(ModuleOptions options) {
Mvid.Value = options.Mvid;
EncId.Value = options.EncId;
EncBaseId.Value = options.EncBaseId;
Name = options.Name;
ModuleKind.SelectedItem = options.Kind;
DllCharacteristics = options.DllCharacteristics;
RuntimeVersion = options.RuntimeVersion;
Machine.SelectedItem = options.Machine;
Cor20HeaderFlags = options.Cor20HeaderFlags;
Cor20HeaderRuntimeVersion.Value = options.Cor20HeaderRuntimeVersion;
TablesHeaderVersion.Value = options.TablesHeaderVersion;
ManagedEntryPoint = options.ManagedEntryPoint;
NativeEntryPointRva.Value = (uint)options.NativeEntryPoint;
if (options.ManagedEntryPoint is not null)
EntryPointEnum = EntryPointType.Managed;
else if (options.NativeEntryPoint != 0)
EntryPointEnum = EntryPointType.Native;
else
EntryPointEnum = EntryPointType.None;
// Writing to Machine and ModuleKind triggers code that updates Characteristics so write
// this property last.
Characteristics = options.Characteristics;
CustomAttributesVM.InitializeFrom(options.CustomAttributes);
}
ModuleOptions CopyTo(ModuleOptions options) {
options.Mvid = Mvid.Value;
options.EncId = EncId.Value;
options.EncBaseId = EncBaseId.Value;
options.Name = Name;
options.Kind = (ModuleKind)ModuleKind.SelectedItem!;
options.Characteristics = Characteristics;
options.DllCharacteristics = DllCharacteristics;
options.RuntimeVersion = RuntimeVersion;
options.Machine = (dnlib.PE.Machine)Machine.SelectedItem!;
options.Cor20HeaderFlags = Cor20HeaderFlags;
options.Cor20HeaderRuntimeVersion = Cor20HeaderRuntimeVersion.Value;
options.TablesHeaderVersion = TablesHeaderVersion.Value;
if (EntryPointEnum == EntryPointType.None) {
options.ManagedEntryPoint = null;
options.NativeEntryPoint = 0;
}
else if (EntryPointEnum == EntryPointType.Managed) {
options.ManagedEntryPoint = ManagedEntryPoint;
options.NativeEntryPoint = 0;
}
else if (EntryPointEnum == EntryPointType.Native) {
options.ManagedEntryPoint = null;
options.NativeEntryPoint = (RVA)NativeEntryPointRva.Value;
}
else
throw new InvalidOperationException();
options.CustomAttributes.Clear();
options.CustomAttributes.AddRange(CustomAttributesVM.Collection.Select(a => a.CreateCustomAttributeOptions().Create()));
return options;
}
void PickManagedEntryPoint() {
if (dnlibTypePicker is null)
throw new InvalidOperationException();
var ep = dnlibTypePicker.GetDnlibType(dnSpy_AsmEditor_Resources.Pick_EntryPoint, new EntryPointDocumentTreeNodeFilter(module), ManagedEntryPoint, module);
if (ep is not null) {
ManagedEntryPoint = ep;
EntryPointEnum = EntryPointType.Managed;
}
}
protected override string? Verify(string columnName) {
if (columnName == nameof(RuntimeVersion))
return SaveModule.MetadataHeaderOptionsVM.ValidateVersionString(options.RuntimeVersion);
return string.Empty;
}
public override bool HasError {
get {
if (!string.IsNullOrEmpty(Verify(nameof(RuntimeVersion))))
return true;
return Mvid.HasError ||
EncId.HasError ||
EncBaseId.HasError ||
Cor20HeaderRuntimeVersion.HasError ||
TablesHeaderVersion.HasError ||
NativeEntryPointRva.HasError;
}
}
}
}