/* 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; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows; using System.Windows.Input; using dnlib.DotNet; using dnSpy.AsmEditor.Properties; using dnSpy.Contracts.App; using dnSpy.Contracts.Controls; using dnSpy.Contracts.Decompiler; using dnSpy.Contracts.Documents; using dnSpy.Contracts.Documents.Tabs; using dnSpy.Contracts.Documents.Tabs.DocViewer; using dnSpy.Contracts.Extension; using dnSpy.Contracts.Images; using dnSpy.Contracts.Menus; using dnSpy.Contracts.Text; using dnSpy.Decompiler.IL; namespace dnSpy.AsmEditor.Commands { [ExportAutoLoaded] sealed class CopyILBytesLoader : IAutoLoaded { static readonly RoutedCommand CopyILBytesCommand = new RoutedCommand("CopyILBytesCommand", typeof(CopyILBytesLoader)); readonly IDocumentTabService documentTabService; readonly Lazy methodAnnotations; [ImportingConstructor] CopyILBytesLoader(IWpfCommandService wpfCommandService, IDocumentTabService documentTabService, Lazy methodAnnotations) { this.documentTabService = documentTabService; this.methodAnnotations = methodAnnotations; var cmds = wpfCommandService.GetCommands(ControlConstants.GUID_DOCUMENTVIEWER_UICONTEXT); cmds.Add(CopyILBytesCommand, CopyILBytesExecuted, CopyILBytesCanExecute, ModifierKeys.Control, Key.B); } void CopyILBytesCanExecute(object? sender, CanExecuteRoutedEventArgs e) => e.CanExecute = CopyILBytesCodeCommand.CanExecute(documentTabService.ActiveTab.TryGetDocumentViewer()); void CopyILBytesExecuted(object? sender, ExecutedRoutedEventArgs e) => CopyILBytesCodeCommand.Execute(documentTabService.ActiveTab.TryGetDocumentViewer(), methodAnnotations); } [ExportMenuItem(Header = "res:CopyILBytesCommand", Icon = DsImagesAttribute.Copy, InputGestureText = "res:CopyILBytesKey", Group = MenuConstants.GROUP_CTX_DOCVIEWER_EDITOR, Order = 20)] sealed class CopyILBytesCodeCommand : MenuItemBase { readonly Lazy methodAnnotations; [ImportingConstructor] CopyILBytesCodeCommand(Lazy methodAnnotations) => this.methodAnnotations = methodAnnotations; public override bool IsVisible(IMenuItemContext context) { if (context.CreatorObject.Guid != new Guid(MenuConstants.GUIDOBJ_DOCUMENTVIEWERCONTROL_GUID)) return false; return CanExecute(context.Find()); } public override void Execute(IMenuItemContext context) { if (context.CreatorObject.Guid == new Guid(MenuConstants.GUIDOBJ_DOCUMENTVIEWERCONTROL_GUID)) Execute(context.Find(), methodAnnotations); } public static bool CanExecute(IDocumentViewer? documentViewer) => documentViewer is not null && FindInstructions(documentViewer).Any(); public static void Execute(IDocumentViewer? documentViewer, Lazy methodAnnotations) { if (!CanExecute(documentViewer)) return; Debug2.Assert(documentViewer is not null); var copier = new InstructionILBytesCopier(); var text = copier.Copy(FindInstructions(documentViewer), methodAnnotations); if (text.Length > 0) { try { Clipboard.SetText(text); } catch (ExternalException) { } if (copier.FoundUnknownBytes) { MsgBox.Instance.ShowIgnorableMessage(new Guid("141A1744-13CD-4835-A804-08D93D8E0D2B"), dnSpy_AsmEditor_Resources.UnknownBytesMsg, MsgBoxButton.OK); } } } static IEnumerable> FindInstructions(IDocumentViewer documentViewer) { foreach (var refInfo in documentViewer.GetSelectedReferences()) { if (refInfo.Data.IsDefinition && refInfo.Data.Reference is InstructionReference) yield return refInfo; } } } struct InstructionILBytesCopier { public bool FoundUnknownBytes { get; private set; } public string Copy(IEnumerable> refs, Lazy methodAnnotations) { var sb = new StringBuilder(); IInstructionBytesReader? reader = null; MethodDef? method = null; int index = 0; foreach (var r in refs) { var ir = (InstructionReference)r.Data.Reference!; var instr = ir.Instruction; if (ir.Method != method) { method = ir.Method; reader = InstructionBytesReader.Create(method, methodAnnotations.Value.IsBodyModified(method)); index = method.Body.Instructions.IndexOf(instr); if (index < 0) throw new InvalidOperationException(); reader.SetInstruction(index, instr.Offset); } else if (index >= method!.Body.Instructions.Count) throw new InvalidOperationException(); else if (method.Body.Instructions[index + 1] != ir.Instruction) { index = method.Body.Instructions.IndexOf(instr); if (index < 0) throw new InvalidOperationException(); reader!.SetInstruction(index, instr.Offset); } else index++; int size = instr.GetSize(); for (int i = 0; i < size; i++) { int b = reader!.ReadByte(); if (b < 0) { sb.Append("??"); FoundUnknownBytes = true; } else sb.Append(b.ToString("X2")); } } return sb.ToString(); } } }