├── DotNetPlugin.Impl ├── Resources │ ├── mcp.png │ └── abouticon.png ├── NativeBindings │ ├── Win32 │ │ ├── Constants.cs │ │ ├── Types.cs │ │ ├── Functions.Psapi.cs │ │ └── Functions.Kernel32.cs │ ├── SDK │ │ ├── Bridge.cs │ │ ├── Bridge.Gui.cs │ │ ├── TitanEngine.cs │ │ └── Bridge.Dbg.cs │ └── Script │ │ ├── Pattern.cs │ │ ├── Register.cs │ │ ├── Disassembly.cs │ │ ├── Gui.cs │ │ ├── Argument.cs │ │ └── Module.cs ├── Plugin.ExpressionFunctions.cs ├── ILRepack.targets ├── Plugin.cs ├── Plugin.Menus.cs ├── DotNetPlugin.Impl.csproj ├── Plugin.EventCallbacks.cs └── Properties │ └── Resources.Designer.cs ├── .editorconfig ├── .gitattributes ├── DotNetPlugin.Stub ├── NativeBindings │ ├── Win32 │ │ ├── Win32Window.cs │ │ ├── Types.WinGdi.cs │ │ ├── Types.cs │ │ └── Types.DebugEvents.cs │ ├── Utf8StringRef.cs │ ├── BlittableBoolean.cs │ ├── StructRef.cs │ ├── SDK │ │ ├── PLog.cs │ │ └── Bridge.cs │ └── Extensions.cs ├── IPluginSession.cs ├── IPlugin.cs ├── Attributes.DllExport.cs ├── DotNetPlugin.Stub.csproj ├── PluginSession.cs ├── PluginBase.cs ├── Commands.cs ├── ExpressionFunctions.cs ├── PluginSessionProxy.cs ├── Menus.cs ├── EventCallbacks.cs └── PluginMain.cs ├── Directory.Build.props ├── DotNetPlugin.RemotingHelper ├── DotNetPlugin.RemotingHelper.csproj └── AppDomainInitializer.cs ├── .github └── workflows │ ├── build-x86.yml │ └── build-x64.yml ├── x64DbgMCPServer.sln ├── .gitignore └── README.md /DotNetPlugin.Impl/Resources/mcp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgentSmithers/x64DbgMCPServer/HEAD/DotNetPlugin.Impl/Resources/mcp.png -------------------------------------------------------------------------------- /DotNetPlugin.Impl/Resources/abouticon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgentSmithers/x64DbgMCPServer/HEAD/DotNetPlugin.Impl/Resources/abouticon.png -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/Win32/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace DotNetPlugin.NativeBindings.Win32 2 | { 3 | public static class Win32Constants 4 | { 5 | public const int MAX_PATH = 260; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; Top-most EditorConfig file 2 | root = true 3 | 4 | ; Windows-style newlines 5 | [*] 6 | end_of_line = CRLF 7 | 8 | ; Tab indentation 9 | [*.cs] 10 | indent_style = space 11 | tab_width = 4 -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/SDK/Bridge.cs: -------------------------------------------------------------------------------- 1 | namespace DotNetPlugin.NativeBindings.SDK 2 | { 3 | // https://github.com/x64dbg/x64dbg/blob/development/src/bridge/bridgemain.h 4 | public sealed partial class Bridge : BridgeBase 5 | { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | DotNetPlugin.Impl/DotNetPlugin.Impl.csproj merge=ours 2 | DotNetPlugin.Stub/PluginMain.cs merge=ours 3 | DotNetPlugin.Stub/Attributes.DllExport.cs merge=ours 4 | .github/workflows/build-x86.yml merge=ours 5 | .github/workflows/build-x64.yml merge=ours 6 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/Plugin.ExpressionFunctions.cs: -------------------------------------------------------------------------------- 1 | namespace DotNetPlugin 2 | { 3 | partial class Plugin 4 | { 5 | [ExpressionFunction] 6 | public static nuint DotNetAdd(nuint a, nuint b) 7 | { 8 | return a + b; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/Win32/Types.cs: -------------------------------------------------------------------------------- 1 | namespace DotNetPlugin.NativeBindings.Win32 2 | { 3 | #pragma warning disable 0649 4 | 5 | public enum ContinueStatus : uint 6 | { 7 | DBG_CONTINUE = 0x00010002, 8 | DBG_EXCEPTION_NOT_HANDLED = 0x80010001, 9 | DBG_REPLY_LATER = 0x40010001 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/NativeBindings/Win32/Win32Window.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace DotNetPlugin.NativeBindings.Win32 5 | { 6 | public sealed class Win32Window : IWin32Window 7 | { 8 | public Win32Window(IntPtr handle) 9 | { 10 | Handle = handle; 11 | } 12 | 13 | public IntPtr Handle { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/IPluginSession.cs: -------------------------------------------------------------------------------- 1 | #if ALLOW_UNLOADING 2 | 3 | using System; 4 | 5 | namespace DotNetPlugin 6 | { 7 | /// 8 | /// Represents the lifecycle of a plugin instance. (Supports Impl assembly unloading.) 9 | /// 10 | internal interface IPluginSession : IPlugin, IDisposable 11 | { 12 | new int PluginHandle { set; } 13 | } 14 | } 15 | 16 | #endif -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/Win32/Functions.Psapi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace DotNetPlugin.NativeBindings.Win32 6 | { 7 | public static class Psapi 8 | { 9 | [DllImport("psapi.dll", CharSet = CharSet.Auto)] 10 | public static extern uint GetModuleBaseName(IntPtr hProcess, IntPtr hModule, StringBuilder lpBaseName, uint nSize); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/IPlugin.cs: -------------------------------------------------------------------------------- 1 | using DotNetPlugin.NativeBindings.SDK; 2 | 3 | namespace DotNetPlugin 4 | { 5 | /// 6 | /// Defines an API to interact with x64dbg. 7 | /// 8 | internal interface IPlugin 9 | { 10 | int PluginHandle { get; } 11 | 12 | bool Init(); 13 | void Setup(ref Plugins.PLUG_SETUPSTRUCT setupStruct); 14 | bool Stop(); 15 | 16 | void OnMenuEntry(ref Plugins.PLUG_CB_MENUENTRY info); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | MCP - Agent Smithers 4 | x64DbgMCPServer 5 | 6 | DotNetPlugin 7 | ..\bin\$(Platform)\$(Configuration)\ 8 | 9 | 9 10 | 11 | 12 | 13 | true 14 | 15 | 16 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/NativeBindings/Utf8StringRef.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DotNetPlugin.NativeBindings 4 | { 5 | [Serializable] 6 | public readonly struct Utf8StringRef 7 | { 8 | private readonly IntPtr _intPtr; 9 | 10 | public Utf8StringRef(IntPtr intPtr) 11 | { 12 | _intPtr = intPtr; 13 | } 14 | 15 | public string GetValue() => _intPtr.MarshalToStringUTF8(); 16 | 17 | public static implicit operator string(Utf8StringRef value) => value.GetValue(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/Win32/Functions.Kernel32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DotNetPlugin.NativeBindings.Win32 5 | { 6 | public static class Kernel32 7 | { 8 | [DllImport("kernel32.dll", EntryPoint = "RtlZeroMemory", ExactSpelling = true)] 9 | public static extern void ZeroMemory(IntPtr dst, nuint length); 10 | 11 | [DllImport("kernel32.dll", SetLastError = true)] 12 | public static extern bool ContinueDebugEvent(int dwProcessId, int dwThreadId, ContinueStatus dwContinueStatus); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/NativeBindings/BlittableBoolean.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DotNetPlugin.NativeBindings 4 | { 5 | // Based on: https://aakinshin.net/posts/blittable/#boolean 6 | [Serializable] 7 | public struct BlittableBoolean 8 | { 9 | private byte _byteValue; 10 | 11 | public bool Value 12 | { 13 | get => Convert.ToBoolean(_byteValue); 14 | set => _byteValue = Convert.ToByte(value); 15 | } 16 | 17 | public static explicit operator BlittableBoolean(bool value) => new BlittableBoolean { Value = value }; 18 | 19 | public static implicit operator bool(BlittableBoolean value) => value.Value; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/ILRepack.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/Attributes.DllExport.cs: -------------------------------------------------------------------------------- 1 | namespace RGiesecke.DllExport 2 | { 3 | using System; 4 | using System.Runtime.InteropServices; 5 | 6 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 7 | public sealed class DllExportAttribute : Attribute 8 | { 9 | public DllExportAttribute() { } 10 | public DllExportAttribute(string entryPoint) { EntryPoint = entryPoint; } 11 | public DllExportAttribute(string entryPoint, CallingConvention callingConvention) 12 | { 13 | EntryPoint = entryPoint; 14 | CallingConvention = callingConvention; 15 | } 16 | 17 | public string EntryPoint { get; } 18 | public CallingConvention CallingConvention { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/NativeBindings/StructRef.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace DotNetPlugin.NativeBindings 5 | { 6 | /// 7 | /// Safe to use with blittable types only! 8 | /// 9 | [Serializable] 10 | public readonly struct StructRef where T : unmanaged 11 | { 12 | private readonly IntPtr _intPtr; 13 | 14 | public StructRef(IntPtr intPtr) 15 | { 16 | _intPtr = intPtr; 17 | } 18 | 19 | public bool HasValue => _intPtr != IntPtr.Zero; 20 | 21 | public ref T Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref _intPtr.ToStructUnsafe(); } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /DotNetPlugin.RemotingHelper/DotNetPlugin.RemotingHelper.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(PluginAssemblyName).RemotingHelper 4 | DotNetPlugin 5 | net472 6 | x86;x64 7 | false 8 | full 9 | true 10 | $(PluginName) 11 | 12 | 13 | 14 | X86;$(DefineConstants) 15 | 16 | 17 | AMD64;$(DefineConstants) 18 | 19 | 20 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/Script/Pattern.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace DotNetPlugin.NativeBindings.Script 4 | { 5 | public static class Pattern 6 | { 7 | #if AMD64 8 | private const string dll = "x64dbg.dll"; 9 | 10 | private const string Script_Pattern_FindMemEP = "?FindMem@Pattern@Script@@YA_K_K0PEBD@Z"; 11 | #else 12 | private const string dll = "x32dbg.dll"; 13 | 14 | private const string Script_Pattern_FindMemEP = "?FindMem@Pattern@Script@@YAKKKPBD@Z"; 15 | #endif 16 | private const CallingConvention cdecl = CallingConvention.Cdecl; 17 | 18 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Pattern_FindMemEP, ExactSpelling = true)] 19 | private static extern nuint Script_Pattern_FindMem(nuint start, nuint size, [MarshalAs(UnmanagedType.LPUTF8Str)] string pattern); 20 | 21 | public static nuint FindMem(nuint start, nuint size, string pattern) => Script_Pattern_FindMem(start, size, pattern); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/Script/Register.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DotNetPlugin.NativeBindings.Script 5 | { 6 | public static class Register 7 | { 8 | #if AMD64 9 | private const string dll = "x64dbg.dll"; 10 | 11 | private const string ScriptRegisterGetCIP = "?GetCIP@Register@Script@@YA_KXZ"; 12 | private const string ScriptRegisterGetCSP = "?GetCSP@Register@Script@@YA_KXZ"; 13 | #else 14 | private const string dll = "x32dbg.dll"; 15 | 16 | private const string ScriptRegisterGetCIP = "?GetCIP@Register@Script@@YAKXZ"; 17 | private const string ScriptRegisterGetCSP = "?GetCSP@Register@Script@@YAKXZ"; 18 | #endif 19 | private const CallingConvention cdecl = CallingConvention.Cdecl; 20 | 21 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = ScriptRegisterGetCIP, ExactSpelling = true)] 22 | public static extern UIntPtr GetCIP(); 23 | 24 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = ScriptRegisterGetCSP, ExactSpelling = true)] 25 | public static extern UIntPtr GetCSP(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/Script/Disassembly.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace DotNetPlugin.NativeBindings.Script 4 | { 5 | public static partial class Gui 6 | { 7 | public static class Disassembly 8 | { 9 | #if AMD64 10 | private const string dll = "x64dbg.dll"; 11 | 12 | private const string Script_Gui_Disassembly_SelectionGetStartEP = "?SelectionGetStart@Disassembly@Gui@Script@@YA_KXZ"; 13 | private const string Script_Gui_Disassembly_SelectionGetEndEP = "?SelectionGetEnd@Disassembly@Gui@Script@@YA_KXZ"; 14 | #else 15 | private const string dll = "x32dbg.dll"; 16 | 17 | private const string Script_Gui_Disassembly_SelectionGetStartEP = "?SelectionGetStart@Disassembly@Gui@Script@@YAKXZ"; 18 | private const string Script_Gui_Disassembly_SelectionGetEndEP = "?SelectionGetEnd@Disassembly@Gui@Script@@YAKXZ"; 19 | #endif 20 | private const CallingConvention cdecl = CallingConvention.Cdecl; 21 | 22 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Gui_Disassembly_SelectionGetStartEP, ExactSpelling = true)] 23 | private static extern nuint Script_Gui_Disassembly_SelectionGetStart(); 24 | 25 | public static nuint SelectionGetStart() => Script_Gui_Disassembly_SelectionGetStart(); 26 | 27 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Gui_Disassembly_SelectionGetEndEP, ExactSpelling = true)] 28 | private static extern nuint Script_Gui_Disassembly_SelectionGetEnd(); 29 | 30 | public static nuint SelectionGetEnd() => Script_Gui_Disassembly_SelectionGetEnd(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/build-x86.yml: -------------------------------------------------------------------------------- 1 | name: Build x86 Plugin 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | paths: 7 | - '**/*.cs' 8 | - '**/*.csproj' 9 | - 'Directory.Build.props' 10 | - '.github/workflows/build-x86.yml' 11 | 12 | jobs: 13 | build: 14 | runs-on: windows-latest 15 | env: 16 | NUGET_PACKAGES: ${{ github.workspace }}\\packages 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Setup MSBuild 22 | uses: microsoft/setup-msbuild@v2 23 | 24 | - name: Setup .NET SDK 25 | uses: actions/setup-dotnet@v4 26 | with: 27 | dotnet-version: '8.0.x' 28 | 29 | - name: Restore 30 | run: msbuild x64DbgMCPServer.sln /t:Restore /p:Platform=x86 /p:Configuration=Debug /p:RestorePackagesPath="${{ env.NUGET_PACKAGES }}" /p:BaseIntermediateOutputPath=.cache\\obj\\ 31 | 32 | - name: Build x86 Debug 33 | run: msbuild x64DbgMCPServer.sln /t:Rebuild /p:Platform=x86 /p:Configuration=Debug /p:RestorePackagesPath="${{ env.NUGET_PACKAGES }}" /p:BaseIntermediateOutputPath=.cache\\obj\\ /p:BaseOutputPath=.cache\\bin\\ 34 | 35 | - name: Upload artifact (dp32) 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: AgentSmithers_x64DbgMCP_x32Plugin 39 | path: | 40 | bin\\x86\\Debug\\**\\*.dp32 41 | bin\\x86\\Debug\\**\\*.dll 42 | bin\\x86\\Debug\\**\\*.pdb 43 | if-no-files-found: error 44 | 45 | - name: Cleanup caches 46 | if: always() 47 | run: | 48 | Remove-Item -Recurse -Force .cache -ErrorAction SilentlyContinue 49 | Remove-Item -Recurse -Force packages -ErrorAction SilentlyContinue 50 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/Script/Gui.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace DotNetPlugin.NativeBindings.Script 4 | { 5 | public static partial class Gui 6 | { 7 | public enum Window 8 | { 9 | DisassemblyWindow, 10 | DumpWindow, 11 | StackWindow, 12 | GraphWindow, 13 | MemMapWindow, 14 | SymModWindow 15 | }; 16 | 17 | #if AMD64 18 | private const string dll = "x64dbg.dll"; 19 | 20 | private const string Script_Gui_SelectionGetStartEP = "?SelectionGetStart@Gui@Script@@YA_KW4Window@12@@Z"; 21 | private const string Script_Gui_SelectionGetEndEP = "?SelectionGetEnd@Gui@Script@@YA_KW4Window@12@@Z"; 22 | #else 23 | private const string dll = "x32dbg.dll"; 24 | 25 | private const string Script_Gui_SelectionGetStartEP = "?SelectionGetStart@Gui@Script@@YAKW4Window@12@@Z"; 26 | private const string Script_Gui_SelectionGetEndEP = "?SelectionGetEnd@Gui@Script@@YAKW4Window@12@@Z"; 27 | #endif 28 | private const CallingConvention cdecl = CallingConvention.Cdecl; 29 | 30 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Gui_SelectionGetStartEP, ExactSpelling = true)] 31 | private static extern nuint Script_Gui_SelectionGetStart(Window window); 32 | 33 | public static nuint SelectionGetStart(Window window) => Script_Gui_SelectionGetStart(window); 34 | 35 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Gui_SelectionGetEndEP, ExactSpelling = true)] 36 | private static extern nuint Script_Gui_SelectionGetEnd(Window window); 37 | 38 | public static nuint SelectionGetEnd(Window window) => Script_Gui_SelectionGetEnd(window); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/build-x64.yml: -------------------------------------------------------------------------------- 1 | name: Build x64 Plugin 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | paths: 7 | - '**/*.cs' 8 | - '**/*.csproj' 9 | - 'Directory.Build.props' 10 | - '.github/workflows/build-x64.yml' 11 | 12 | jobs: 13 | build: 14 | runs-on: windows-latest 15 | env: 16 | NUGET_PACKAGES: ${{ github.workspace }}\\packages 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Setup MSBuild 22 | uses: microsoft/setup-msbuild@v2 23 | 24 | - name: Setup .NET SDK 25 | uses: actions/setup-dotnet@v4 26 | with: 27 | dotnet-version: '8.0.x' 28 | 29 | - name: Restore 30 | run: msbuild x64DbgMCPServer.sln /t:Restore /p:Platform=x64 /p:Configuration=Debug /p:RestorePackagesPath="${{ env.NUGET_PACKAGES }}" /p:BaseIntermediateOutputPath=.cache\\obj\\ 31 | 32 | - name: Build x64 Debug 33 | run: msbuild x64DbgMCPServer.sln /t:Rebuild /p:Platform=x64 /p:Configuration=Debug /p:RestorePackagesPath="${{ env.NUGET_PACKAGES }}" /p:BaseIntermediateOutputPath=.cache\\obj\\ /p:BaseOutputPath=.cache\\bin\\ 34 | 35 | - name: Upload artifact (dp64) 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: AgentSmithers_x64DbgMCP_x64Plugin 39 | path: | 40 | bin\\x64\\Debug\\**\\*.dp64 41 | bin\\x64\\Debug\\**\\*.dll 42 | bin\\x64\\Debug\\**\\*.pdb 43 | if-no-files-found: error 44 | 45 | - name: Cleanup caches 46 | if: always() 47 | run: | 48 | Remove-Item -Recurse -Force .cache -ErrorAction SilentlyContinue 49 | Remove-Item -Recurse -Force packages -ErrorAction SilentlyContinue 50 | 51 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/Script/Argument.cs: -------------------------------------------------------------------------------- 1 | using DotNetPlugin.NativeBindings.SDK; 2 | using System; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace DotNetPlugin.NativeBindings.Script 6 | { 7 | public static class Argument 8 | { 9 | [Serializable] 10 | public unsafe struct ArgumentInfo 11 | { 12 | private fixed byte _mod[BridgeBase.MAX_MODULE_SIZE]; 13 | public string mod 14 | { 15 | get 16 | { 17 | fixed (byte* ptr = _mod) 18 | return new IntPtr(ptr).MarshalToStringUTF8(Bridge.MAX_MODULE_SIZE); 19 | } 20 | set 21 | { 22 | fixed (byte* ptr = _mod) 23 | value.MarshalToPtrUTF8(new IntPtr(ptr), Bridge.MAX_MODULE_SIZE * 4); 24 | } 25 | } 26 | 27 | nuint rvaStart; 28 | nuint rvaEnd; 29 | bool manual; 30 | nuint instructioncount; 31 | }; 32 | 33 | #if AMD64 34 | private const string dll = "x64dbg.dll"; 35 | 36 | private const string Script_Argument_DeleteRangeEP = "?DeleteRange@Argument@Script@@YAX_K0_N@Z"; 37 | #else 38 | private const string dll = "x32dbg.dll"; 39 | 40 | private const string Script_Argument_DeleteRangeEP = "?DeleteRange@Argument@Script@@YAXKK_N@Z"; 41 | #endif 42 | private const CallingConvention cdecl = CallingConvention.Cdecl; 43 | 44 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Argument_DeleteRangeEP, ExactSpelling = true)] 45 | private static extern void Script_Argument_DeleteRange(nuint start, nuint end, bool deleteManual = false); 46 | 47 | public static void DeleteRange(nuint start, nuint end, bool deleteManual = false) => 48 | Script_Argument_DeleteRange(start, end, deleteManual); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /DotNetPlugin.RemotingHelper/AppDomainInitializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | 5 | namespace DotNetPlugin 6 | { 7 | /// 8 | /// A helper class which enables the Stub assembly to be resolved in a separate app domain. 9 | /// 10 | /// 11 | /// It's inevitable to place this class into a separate assembly because of an issue of the remoting activator: 12 | /// if this type resided in the Stub assembly, the activator would want to load that assembly in the app domain upon initialization, 13 | /// which would fail because the activator looks for a dll but x64dbg plugins must have a custom extension (dp32/dp64)... 14 | /// 15 | public static class AppDomainInitializer 16 | { 17 | private const string DllExtension = 18 | #if AMD64 19 | ".dp64"; 20 | #else 21 | ".dp32"; 22 | #endif 23 | 24 | public static void Initialize(string[] args) 25 | { 26 | AppDomain.CurrentDomain.AssemblyResolve += (s, e) => 27 | { 28 | var assemblyName = new AssemblyName(e.Name); 29 | var pluginAssemblyName = typeof(AppDomainInitializer).Assembly.GetName().Name; 30 | 31 | if (pluginAssemblyName.StartsWith(assemblyName.Name, StringComparison.OrdinalIgnoreCase) && 32 | pluginAssemblyName.Substring(assemblyName.Name.Length).Equals(".RemotingHelper", StringComparison.OrdinalIgnoreCase)) 33 | { 34 | var location = typeof(AppDomainInitializer).Assembly.Location; 35 | var pluginBasePath = Path.GetDirectoryName(location); 36 | var dllPath = Path.Combine(pluginBasePath, assemblyName.Name + DllExtension); 37 | 38 | return Assembly.LoadFile(dllPath); 39 | } 40 | 41 | return null; 42 | }; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/Plugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using DotNetPlugin.NativeBindings; 4 | using DotNetPlugin.NativeBindings.SDK; 5 | 6 | namespace DotNetPlugin 7 | { 8 | /// 9 | /// Implementation of your x64dbg plugin. 10 | /// 11 | /// 12 | /// If you change the namespace or name of this class, don't forget to reflect the change in too! 13 | /// 14 | public partial class Plugin : PluginBase 15 | { 16 | public override bool Init() 17 | { 18 | Console.SetOut(PLogTextWriter.Default); 19 | Console.SetError(PLogTextWriter.Default); 20 | 21 | LogInfo($"PluginHandle: {PluginHandle}"); 22 | 23 | 24 | // You can listen to debugger events in two ways: 25 | // 1. by declaring dll exports in the Stub project (see PluginMain), then adding the corresponding methods to the IPlugin interface, 26 | // finally implementing them as required to propagate the call to the Plugin class or 27 | // 2. by registering callbacks using the EventCallback attribute (see Plugin.EventCallbacks.cs). 28 | 29 | // Please note that Option 1 goes through remoting in Debug builds (where Impl assembly unloading is enabled), 30 | // so it may be somewhat slower than Option 2. Release builds don't use remoting, just direct calls, so in that case there should be no significant difference. 31 | 32 | // Commands and function expressions are discovered and registered automatically. See Plugin.Commands.cs and Plugin.ExpressionFunctions.cs. 33 | 34 | // Menus can be registered by overriding the SetupMenu method. See Plugin.Menus.cs. 35 | 36 | return true; 37 | } 38 | 39 | public override void Setup(ref Plugins.PLUG_SETUPSTRUCT setupStruct) 40 | { 41 | // Do additional UI setup (apart from menus) here. 42 | Plugin.cbStartMCPServer(null); 43 | } 44 | 45 | public override Task StopAsync() 46 | { 47 | // Do additional cleanup here. 48 | 49 | return Task.FromResult(true); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/NativeBindings/SDK/PLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using DotNetPlugin.NativeBindings.SDK; 5 | 6 | namespace DotNetPlugin.NativeBindings 7 | { 8 | public sealed class PLogTextWriter : TextWriter 9 | { 10 | public static readonly PLogTextWriter Default = new PLogTextWriter(); 11 | 12 | private PLogTextWriter() 13 | { 14 | NewLine = "\n"; 15 | } 16 | 17 | public override Encoding Encoding => Encoding.UTF8; 18 | 19 | public override void Write(char value) => 20 | Write(value.ToString()); 21 | 22 | public override void Write(char[] buffer, int index, int count) => 23 | Write(new string(buffer, index, count)); 24 | 25 | public override void Write(string value) => 26 | Write(value, Array.Empty()); 27 | 28 | public override void Write(string format, object arg0) => 29 | Write(format, new[] { arg0 }); 30 | 31 | public override void Write(string format, object arg0, object arg1) => 32 | Write(format, new[] { arg0, arg1 }); 33 | 34 | public override void Write(string format, object arg0, object arg1, object arg2) => 35 | Write(format, new[] { arg0, arg1, arg2 }); 36 | 37 | public override void Write(string format, params object[] args) => 38 | Plugins._plugin_logprint(string.Format(format, args)); 39 | 40 | public override void WriteLine(char value) => 41 | WriteLine(value.ToString()); 42 | 43 | public override void WriteLine(char[] buffer, int index, int count) => 44 | WriteLine(new string(buffer, index, count)); 45 | 46 | public override void WriteLine(string value) => 47 | WriteLine(value, Array.Empty()); 48 | 49 | public override void WriteLine(string format, object arg0) => 50 | WriteLine(format, new[] { arg0 }); 51 | 52 | public override void WriteLine(string format, object arg0, object arg1) => 53 | WriteLine(format, new[] { arg0, arg1 }); 54 | 55 | public override void WriteLine(string format, object arg0, object arg1, object arg2) => 56 | WriteLine(format, new[] { arg0, arg1, arg2 }); 57 | 58 | public override void WriteLine(string format, params object[] args) => 59 | Write(format + NewLine, args); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/SDK/Bridge.Gui.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DotNetPlugin.NativeBindings.SDK 5 | { 6 | // https://github.com/x64dbg/x64dbg/blob/development/src/bridge/bridgemain.h 7 | partial class Bridge 8 | { 9 | public const int GUI_MAX_LINE_SIZE = 65536; 10 | 11 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 12 | private static extern bool GuiGetLineWindow([MarshalAs(UnmanagedType.LPUTF8Str)] string title, IntPtr text); 13 | 14 | public static unsafe bool GuiGetLineWindow([MarshalAs(UnmanagedType.LPUTF8Str)] string title, out string text) 15 | { 16 | // alternatively we could implement a custom marshaler (ICustomMarshaler) but that wont't work for ref/out parameters for some reason... 17 | var textBuffer = Marshal.AllocHGlobal(GUI_MAX_LINE_SIZE); 18 | try 19 | { 20 | var success = GuiGetLineWindow(title, textBuffer); 21 | text = success ? textBuffer.MarshalToStringUTF8(GUI_MAX_LINE_SIZE) : default; 22 | return success; 23 | } 24 | finally { Marshal.FreeHGlobal(textBuffer); } 25 | } 26 | 27 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 28 | public static extern void GuiAddStatusBarMessage([MarshalAs(UnmanagedType.LPUTF8Str)] string msg); 29 | 30 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 31 | public static extern void GuiLogClear(); 32 | 33 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 34 | public static extern void GuiAddLogMessage([MarshalAs(UnmanagedType.LPUTF8Str)] string msg); 35 | 36 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 37 | public static extern void GuiUpdateDisassemblyView(); 38 | 39 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 40 | private static extern bool GuiGetDisassembly(nuint addr, IntPtr text); 41 | 42 | public static unsafe bool GuiGetDisassembly(nuint addr, out string text) 43 | { 44 | var textBuffer = Marshal.AllocHGlobal(GUI_MAX_LINE_SIZE); 45 | try 46 | { 47 | var success = GuiGetDisassembly(addr, textBuffer); 48 | text = success ? textBuffer.MarshalToStringUTF8(GUI_MAX_LINE_SIZE) : default; 49 | return success; 50 | } 51 | finally 52 | { 53 | Marshal.FreeHGlobal(textBuffer); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/NativeBindings/Win32/Types.WinGdi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DotNetPlugin.NativeBindings.Win32 5 | { 6 | #pragma warning disable 0649 7 | 8 | [Serializable] 9 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 10 | public struct BITMAPFILEHEADER 11 | { 12 | public ushort bfType; 13 | public uint bfSize; 14 | public ushort bfReserved1; 15 | public ushort bfReserved2; 16 | public uint bfOffBits; 17 | } 18 | 19 | [Serializable] 20 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 21 | public struct BITMAPV5HEADER 22 | { 23 | public uint bV5Size; 24 | public int bV5Width; 25 | public int bV5Height; 26 | public ushort bV5Planes; 27 | public ushort bV5BitCount; 28 | public BitmapCompressionMode bV5Compression; 29 | public uint bV5SizeImage; 30 | public int bV5XPelsPerMeter; 31 | public int bV5YPelsPerMeter; 32 | public uint bV5ClrUsed; 33 | public uint bV5ClrImportant; 34 | public uint bV5RedMask; 35 | public uint bV5GreenMask; 36 | public uint bV5BlueMask; 37 | public uint bV5AlphaMask; 38 | public LCSCSTYPE bV5CSType; 39 | public CIEXYZTRIPLE bV5Endpoints; 40 | public uint bV5GammaRed; 41 | public uint bV5GammaGreen; 42 | public uint bV5GammaBlue; 43 | public LCSGAMUTMATCH bV5Intent; 44 | public uint bV5ProfileData; 45 | public uint bV5ProfileSize; 46 | public uint bV5Reserved; 47 | } 48 | 49 | public enum BitmapCompressionMode : uint 50 | { 51 | BI_RGB = 0, 52 | BI_RLE8 = 1, 53 | BI_RLE4 = 2, 54 | BI_BITFIELDS = 3, 55 | BI_JPEG = 4, 56 | BI_PNG = 5 57 | } 58 | 59 | [Serializable] 60 | public struct CIEXYZTRIPLE 61 | { 62 | public CIEXYZ ciexyzRed; 63 | public CIEXYZ ciexyzGreen; 64 | public CIEXYZ ciexyzBlue; 65 | } 66 | 67 | [Serializable] 68 | public struct CIEXYZ 69 | { 70 | public int ciexyzX; 71 | public int ciexyzY; 72 | public int ciexyzZ; 73 | } 74 | 75 | public enum LCSCSTYPE : uint 76 | { 77 | LCS_CALIBRATED_RGB = 0, 78 | LCS_sRGB = 0x73524742, 79 | LCS_WINDOWS_COLOR_SPACE = 0x57696e20, 80 | } 81 | 82 | [Flags] 83 | public enum LCSGAMUTMATCH : uint 84 | { 85 | LCS_GM_BUSINESS = 0x00000001, 86 | LCS_GM_GRAPHICS = 0x00000002, 87 | LCS_GM_IMAGES = 0x00000004, 88 | LCS_GM_ABS_COLORIMETRIC = 0x00000008, 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/Plugin.Menus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Windows.Forms; 4 | using DotNetPlugin.NativeBindings.SDK; 5 | using DotNetPlugin.Properties; 6 | 7 | namespace DotNetPlugin 8 | { 9 | partial class Plugin 10 | { 11 | protected override void SetupMenu(Menus menus) 12 | { 13 | // Set main plugin menu icon (PNG resource only) 14 | try 15 | { 16 | menus.Main.SetIcon(Resources.MainIcon); 17 | } 18 | catch 19 | { 20 | // Last resort: keep default without icon 21 | } 22 | 23 | menus.Main 24 | .AddAndConfigureItem("&Start MCP Server", StartMCPServer).SetIcon(Resources.AboutIcon).Parent 25 | .AddAndConfigureItem("&Stop MCP Server", StopMCPServer).SetIcon(Resources.AboutIcon).Parent 26 | .AddAndConfigureItem("&About...", OnAboutMenuItem).SetIcon(Resources.AboutIcon); 27 | //.AddAndConfigureItem("&CustomCommand", ExecuteCustomCommand).SetIcon(Resources.AboutIcon).Parent 28 | //.AddAndConfigureItem("&DotNetDumpProcess", OnDumpMenuItem).SetHotKey("CTRL+F12").Parent 29 | //.AddAndConfigureSubMenu("sub menu") 30 | // .AddItem("sub menu entry1", menuItem => Console.WriteLine($"hEntry={menuItem.Id}")) 31 | // .AddSeparator() 32 | // .AddItem("sub menu entry2", menuItem => Console.WriteLine($"hEntry={menuItem.Id}")); 33 | } 34 | 35 | public void OnAboutMenuItem(MenuItem menuItem) 36 | { 37 | MessageBox.Show(HostWindow, "x64DbgMCPServer Plugin For x64dbg\nCoded By AgentSmithers", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); 38 | } 39 | 40 | public static void OnDumpMenuItem(MenuItem menuItem) 41 | { 42 | if (!Bridge.DbgIsDebugging()) 43 | { 44 | Console.WriteLine("You need to be debugging to use this Command"); 45 | return; 46 | } 47 | Bridge.DbgCmdExec("DotNetDumpProcess"); 48 | } 49 | 50 | public static void ExecuteCustomCommand(MenuItem menuItem) 51 | { 52 | if (!Bridge.DbgIsDebugging()) 53 | { 54 | Console.WriteLine("You need to be debugging to use this Command"); 55 | return; 56 | } 57 | Bridge.DbgCmdExec("DumpModuleToFile"); 58 | } 59 | public static void StartMCPServer(MenuItem menuItem) 60 | { 61 | Bridge.DbgCmdExec("StartMCPServer"); 62 | } 63 | public static void StopMCPServer(MenuItem menuItem) 64 | { 65 | Bridge.DbgCmdExec("StopMCPServer"); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/NativeBindings/Win32/Types.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DotNetPlugin.NativeBindings.Win32 5 | { 6 | #pragma warning disable 0649 7 | 8 | [Serializable] 9 | public struct MSG 10 | { 11 | public IntPtr hwnd; 12 | public uint message; 13 | public UIntPtr wParam; 14 | public IntPtr lParam; 15 | public uint time; 16 | public POINT pt; 17 | public uint lPrivate; 18 | } 19 | 20 | [Serializable] 21 | public struct POINT 22 | { 23 | public int X; 24 | public int Y; 25 | } 26 | 27 | [Serializable] 28 | public struct IMAGEHLP_MODULE64 29 | { 30 | public uint SizeOfStruct; 31 | public ulong BaseOfImage; 32 | public uint ImageSize; 33 | public uint TimeDateStamp; 34 | public uint CheckSum; 35 | public uint NumSyms; 36 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] 37 | public SYM_TYPE[] SymType; 38 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 39 | public string ModuleName; 40 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 41 | public string ImageName; 42 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 43 | public string LoadedImageName; 44 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 45 | public string LoadedPdbName; 46 | public uint CVSig; 47 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 780)] 48 | public string CVData; 49 | public uint PdbSig; 50 | public GUID PdbSig70; 51 | public uint PdbAge; 52 | public int PdbUnmatched; 53 | public int DbgUnmatched; 54 | public int LineNumbers; 55 | public int GlobalSymbols; 56 | public int TypeInfo; 57 | public int SourceIndexed; 58 | public int Publics; 59 | } 60 | 61 | public enum SYM_TYPE 62 | { 63 | SymNone, 64 | SymCoff, 65 | SymCv, 66 | SymPdb, 67 | SymExport, 68 | SymDeferred, 69 | SymSym, 70 | SymDia, 71 | SymVirtual, 72 | NumSymTypes, 73 | } 74 | 75 | [Serializable] 76 | public struct GUID 77 | { 78 | public uint Data1; 79 | public ushort Data2; 80 | public ushort Data3; 81 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 82 | public byte[] Data4; 83 | } 84 | 85 | [Serializable] 86 | public struct PROCESS_INFORMATION 87 | { 88 | public IntPtr hProcess; 89 | public IntPtr hThread; 90 | public uint dwProcessId; 91 | public uint dwThreadId; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/DotNetPlugin.Stub.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(PluginAssemblyName) 4 | $(PluginRootNamespace) 5 | net472 6 | x86;x64 7 | false 8 | full 9 | true 10 | true 11 | $(PluginName) 12 | 13 | 14 | 15 | X86;$(DefineConstants) 16 | .dp32 17 | 18 | 19 | AMD64;$(DefineConstants) 20 | .dp64 21 | 22 | 23 | 24 | 25 | 26 | ALLOW_UNLOADING;$(DefineConstants) 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | build.meta 49 | $(IntermediateOutputPath)$(BuildMetadataFileName) 50 | $([System.IO.Path]::GetFullPath($(PluginOutputPath)))$(TargetName).Impl$(TargetExt) 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | $(BuildMetadataFileName) 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | build 71 | 72 | 73 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/PluginSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Runtime.CompilerServices; 5 | using System.Threading; 6 | using DotNetPlugin.NativeBindings; 7 | using DotNetPlugin.NativeBindings.SDK; 8 | 9 | namespace DotNetPlugin 10 | { 11 | /// 12 | /// Manages the lifecycle of a plugin instance and forward calls to it while it's alive. 13 | /// 14 | internal sealed class PluginSession : 15 | #if ALLOW_UNLOADING 16 | MarshalByRefObject, IPluginSession 17 | #else 18 | IPlugin 19 | #endif 20 | { 21 | internal static PluginSession Null = new PluginSession(PluginBase.Null); 22 | 23 | private static Type GetPluginType(string pluginTypeName, string implAssemblyName) 24 | { 25 | #if ALLOW_UNLOADING 26 | if (PluginMain.ImplAssemblyLocation != null) 27 | { 28 | Assembly implAssembly; 29 | 30 | try 31 | { 32 | var rawAssembly = File.ReadAllBytes(PluginMain.ImplAssemblyLocation); 33 | implAssembly = Assembly.Load(rawAssembly); 34 | } 35 | catch { implAssembly = null; } 36 | 37 | if (implAssembly != null) 38 | return implAssembly.GetType(pluginTypeName, throwOnError: true); 39 | } 40 | #endif 41 | 42 | return Type.GetType(pluginTypeName + ", " + implAssemblyName, throwOnError: true); 43 | } 44 | 45 | private static PluginBase CreatePlugin() 46 | { 47 | var implAssemblyName = typeof(PluginMain).Assembly.GetName().Name; 48 | #if ALLOW_UNLOADING 49 | implAssemblyName += ".Impl"; 50 | #endif 51 | var pluginTypeName = typeof(PluginMain).Namespace + ".Plugin"; 52 | var pluginType = GetPluginType(pluginTypeName, implAssemblyName); 53 | return (PluginBase)Activator.CreateInstance(pluginType); 54 | } 55 | 56 | #if ALLOW_UNLOADING 57 | private volatile PluginBase _plugin; 58 | #else 59 | private readonly PluginBase _plugin; 60 | #endif 61 | 62 | private PluginSession(PluginBase plugin) 63 | { 64 | _plugin = plugin; 65 | } 66 | 67 | public PluginSession() : this(CreatePlugin()) { } 68 | 69 | #if ALLOW_UNLOADING 70 | public void Dispose() => Stop(); 71 | 72 | // https://stackoverflow.com/questions/2410221/appdomain-and-marshalbyrefobject-life-time-how-to-avoid-remotingexception 73 | public override object InitializeLifetimeService() => null; 74 | #endif 75 | 76 | public int PluginHandle 77 | { 78 | get => _plugin.PluginHandle; 79 | set => _plugin.PluginHandle = value; 80 | } 81 | 82 | public bool Init() => _plugin.InitInternal(); 83 | public void Setup(ref Plugins.PLUG_SETUPSTRUCT setupStruct) => _plugin.SetupInternal(ref setupStruct); 84 | public bool Stop() 85 | { 86 | #if ALLOW_UNLOADING 87 | var plugin = Interlocked.Exchange(ref _plugin, PluginBase.Null); 88 | 89 | if (plugin == PluginBase.Null) 90 | return true; 91 | #else 92 | var plugin = _plugin; 93 | #endif 94 | 95 | return plugin.Stop(); 96 | } 97 | 98 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 99 | public void OnMenuEntry(ref Plugins.PLUG_CB_MENUENTRY info) => ((IPlugin)_plugin).OnMenuEntry(ref info); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/PluginBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Threading.Tasks; 4 | using DotNetPlugin.NativeBindings; 5 | using DotNetPlugin.NativeBindings.SDK; 6 | using DotNetPlugin.NativeBindings.Win32; 7 | 8 | namespace DotNetPlugin 9 | { 10 | /// 11 | /// Provides a base class from which the Plugin class must derive in the Impl assembly. 12 | /// 13 | public class PluginBase : IPlugin 14 | { 15 | internal static PluginBase Null = new PluginBase(); 16 | 17 | public static readonly string PluginName = 18 | typeof(PluginMain).Assembly.GetCustomAttribute()?.Title ?? 19 | typeof(PluginMain).Assembly.GetName().Name; 20 | 21 | public static readonly int PluginVersion = typeof(PluginMain).Assembly.GetName().Version.Major; 22 | 23 | private static readonly string PluginLogPrefix = $"[PLUGIN, {PluginName}]"; 24 | 25 | public static void LogInfo(string message) => PLogTextWriter.Default.WriteLine(PluginLogPrefix + " " + message); 26 | public static void LogError(string message) => LogInfo(message); 27 | 28 | IDisposable _commandRegistrations; 29 | IDisposable _expressionFunctionRegistrations; 30 | IDisposable _eventCallbackRegistrations; 31 | Menus _menus; 32 | 33 | protected PluginBase() { } 34 | 35 | protected object MenusSyncObj => _menus; 36 | public int PluginHandle { get; internal set; } 37 | public Win32Window HostWindow { get; private set; } 38 | 39 | internal bool InitInternal() 40 | { 41 | var pluginMethods = GetType().GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 42 | 43 | _commandRegistrations = Commands.Initialize(this, pluginMethods); 44 | _expressionFunctionRegistrations = ExpressionFunctions.Initialize(this, pluginMethods); 45 | _eventCallbackRegistrations = EventCallbacks.Initialize(this, pluginMethods); 46 | return Init(); 47 | } 48 | 49 | public virtual bool Init() => true; 50 | 51 | protected virtual void SetupMenu(Menus menus) { } 52 | 53 | internal void SetupInternal(ref Plugins.PLUG_SETUPSTRUCT setupStruct) 54 | { 55 | HostWindow = new Win32Window(setupStruct.hwndDlg); 56 | 57 | _menus = new Menus(PluginHandle, ref setupStruct); 58 | 59 | try { SetupMenu(_menus); } 60 | catch (MenuException ex) 61 | { 62 | LogError($"Registration of menu failed. {ex.Message}"); 63 | _menus.Clear(); 64 | } 65 | 66 | Setup(ref setupStruct); 67 | } 68 | 69 | public virtual void Setup(ref Plugins.PLUG_SETUPSTRUCT setupStruct) { } 70 | 71 | public bool Stop() 72 | { 73 | try 74 | { 75 | var stopTask = StopAsync(); 76 | 77 | if (Task.WhenAny(stopTask, Task.Delay(5000)).GetAwaiter().GetResult() == stopTask) 78 | { 79 | if (!stopTask.IsCanceled) 80 | return stopTask.ConfigureAwait(false).GetAwaiter().GetResult(); // also unwraps potential exception 81 | } 82 | } 83 | catch (Exception ex) 84 | { 85 | PluginMain.LogUnhandledException(ex); 86 | } 87 | finally 88 | { 89 | _menus.Dispose(); 90 | _eventCallbackRegistrations.Dispose(); 91 | _expressionFunctionRegistrations.Dispose(); 92 | _commandRegistrations.Dispose(); 93 | } 94 | 95 | return false; 96 | } 97 | 98 | public virtual Task StopAsync() => Task.FromResult(true); 99 | 100 | void IPlugin.OnMenuEntry(ref Plugins.PLUG_CB_MENUENTRY info) 101 | { 102 | MenuItem menuItem; 103 | lock (MenusSyncObj) 104 | { 105 | menuItem = _menus.GetMenuItemById(info.hEntry); 106 | } 107 | menuItem?.Handler(menuItem); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /x64DbgMCPServer.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.13.35806.99 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetPlugin.Stub", "DotNetPlugin.Stub\DotNetPlugin.Stub.csproj", "{F2F8BA6A-0112-47CA-9AFA-E8082263AB72}" 6 | EndProject 7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetPlugin.Impl", "DotNetPlugin.Impl\DotNetPlugin.Impl.csproj", "{3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}" 8 | EndProject 9 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetPlugin.RemotingHelper", "DotNetPlugin.RemotingHelper\DotNetPlugin.RemotingHelper.csproj", "{CF3CB686-DD3D-4782-9192-8195A968DC09}" 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Global", "Global", "{93EAEA47-7B3E-4F65-9D11-A2C8F00071DD}" 12 | ProjectSection(SolutionItems) = preProject 13 | Directory.Build.props = Directory.Build.props 14 | EndProjectSection 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Debug|x64 = Debug|x64 20 | Debug|x86 = Debug|x86 21 | Release|Any CPU = Release|Any CPU 22 | Release|x64 = Release|x64 23 | Release|x86 = Release|x86 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Debug|Any CPU.ActiveCfg = Debug|x64 27 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Debug|Any CPU.Build.0 = Debug|x64 28 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Debug|x64.ActiveCfg = Debug|x64 29 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Debug|x64.Build.0 = Debug|x64 30 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Debug|x86.ActiveCfg = Debug|x86 31 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Debug|x86.Build.0 = Debug|x86 32 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Release|Any CPU.ActiveCfg = Release|x64 33 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Release|Any CPU.Build.0 = Release|x64 34 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Release|x64.ActiveCfg = Release|x64 35 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Release|x64.Build.0 = Release|x64 36 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Release|x86.ActiveCfg = Release|x86 37 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Release|x86.Build.0 = Release|x86 38 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Debug|Any CPU.ActiveCfg = Debug|x64 39 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Debug|Any CPU.Build.0 = Debug|x64 40 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Debug|x64.ActiveCfg = Debug|x64 41 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Debug|x64.Build.0 = Debug|x64 42 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Debug|x86.ActiveCfg = Debug|x86 43 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Debug|x86.Build.0 = Debug|x86 44 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Release|Any CPU.ActiveCfg = Release|x64 45 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Release|Any CPU.Build.0 = Release|x64 46 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Release|x64.ActiveCfg = Release|x64 47 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Release|x64.Build.0 = Release|x64 48 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Release|x86.ActiveCfg = Release|x86 49 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Release|x86.Build.0 = Release|x86 50 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Debug|Any CPU.ActiveCfg = Debug|x64 51 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Debug|Any CPU.Build.0 = Debug|x64 52 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Debug|x64.ActiveCfg = Debug|x64 53 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Debug|x64.Build.0 = Debug|x64 54 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Debug|x86.ActiveCfg = Debug|x86 55 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Debug|x86.Build.0 = Debug|x86 56 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Release|Any CPU.ActiveCfg = Release|x64 57 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Release|Any CPU.Build.0 = Release|x64 58 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Release|x64.ActiveCfg = Release|x64 59 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Release|x64.Build.0 = Release|x64 60 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Release|x86.ActiveCfg = Release|x86 61 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Release|x86.Build.0 = Release|x86 62 | EndGlobalSection 63 | GlobalSection(SolutionProperties) = preSolution 64 | HideSolutionNode = FALSE 65 | EndGlobalSection 66 | GlobalSection(ExtensibilityGlobals) = postSolution 67 | SolutionGuid = {04FC09BA-059D-44FA-A886-0B8E3C34B00F} 68 | EndGlobalSection 69 | EndGlobal 70 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/DotNetPlugin.Impl.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(PluginAssemblyName).Impl 4 | $(PluginRootNamespace) 5 | net472 6 | x86;x64 7 | $(PluginOutputPath) 8 | false 9 | full 10 | true 11 | true 12 | $(PluginName) 13 | bin 14 | 15 | 16 | 17 | X86;$(DefineConstants) 18 | .dp32 19 | 20 | 21 | AMD64;$(DefineConstants) 22 | .dp64 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | build 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | True 52 | True 53 | Resources.resx 54 | 55 | 56 | 57 | 58 | 59 | ResXFileCodeGenerator 60 | Resources.Designer.cs 61 | 62 | 63 | 64 | 74 | 75 | 76 | 77 | $([System.Text.RegularExpressions.Regex]::Replace($(TargetName), '\.Impl$', '')) 78 | .dll 79 | $(TargetDir)$(StubAssemblyName)$(StubAssemblyExt) 80 | $(StubAssemblyPath) 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | C:\Users\User\Desktop\x96\release 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/Commands.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Threading; 6 | using DotNetPlugin.NativeBindings.SDK; 7 | 8 | namespace DotNetPlugin 9 | { 10 | /// 11 | /// Attribute for automatically registering commands in x64Dbg. 12 | /// 13 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] 14 | public class CommandAttribute : Attribute 15 | { 16 | public string Name { get; } 17 | public bool DebugOnly { get; set; } //Command is only visual during an active debug session of a binary. 18 | public bool MCPOnly { get; set; } //Used so it is not registered as an X64Dbg Command 19 | public bool X64DbgOnly { get; set; } //Used so it is not registerd with MCP 20 | public string MCPCmdDescription { get; set; } 21 | 22 | public CommandAttribute() { } 23 | 24 | public CommandAttribute(string name) 25 | { 26 | Name = name; 27 | } 28 | } 29 | 30 | internal static class Commands 31 | { 32 | private static Plugins.CBPLUGINCOMMAND BuildCallback(PluginBase plugin, MethodInfo method, bool reportsSuccess) 33 | { 34 | object firstArg = method.IsStatic ? null : plugin; 35 | 36 | if (reportsSuccess) 37 | { 38 | return (Plugins.CBPLUGINCOMMAND)Delegate.CreateDelegate(typeof(Plugins.CBPLUGINCOMMAND), firstArg, method, throwOnBindFailure: true); 39 | } 40 | else 41 | { 42 | var callback = (Action)Delegate.CreateDelegate(typeof(Action), firstArg, method, throwOnBindFailure: true); 43 | return args => 44 | { 45 | callback(args); 46 | return true; 47 | }; 48 | } 49 | } 50 | 51 | public static IDisposable Initialize(PluginBase plugin, MethodInfo[] pluginMethods) 52 | { 53 | // command names are case-insensitive 54 | var registeredNames = new HashSet(StringComparer.OrdinalIgnoreCase); 55 | 56 | var methods = pluginMethods 57 | .SelectMany(method => method.GetCustomAttributes().Select(attribute => (method, attribute))); 58 | 59 | foreach (var (method, attribute) in methods) 60 | { 61 | var name = attribute.Name ?? method.Name; 62 | 63 | if (attribute.MCPOnly) 64 | { 65 | continue; //Use only for MCPServer remote invokation 66 | } 67 | 68 | var reportsSuccess = method.ReturnType == typeof(bool); 69 | if (!reportsSuccess && method.ReturnType != typeof(void)) 70 | { 71 | PluginBase.LogError($"Registration of command '{name}' is skipped. Method '{method.Name}' has an invalid return type."); 72 | continue; 73 | } 74 | 75 | var methodParams = method.GetParameters(); 76 | 77 | if (methodParams.Length != 1 || methodParams[0].ParameterType != typeof(string[])) 78 | { 79 | PluginBase.LogError($"Registration of command '{name}' is skipped. Method '{method.Name}' has an invalid signature."); 80 | continue; 81 | } 82 | 83 | if (registeredNames.Contains(name) || 84 | !Plugins._plugin_registercommand(plugin.PluginHandle, name, BuildCallback(plugin, method, reportsSuccess), attribute.DebugOnly)) 85 | { 86 | PluginBase.LogError($"Registration of command '{name}' failed."); 87 | continue; 88 | } 89 | 90 | registeredNames.Add(name); 91 | } 92 | 93 | return new Registrations(plugin, registeredNames); 94 | } 95 | 96 | private sealed class Registrations : IDisposable 97 | { 98 | private PluginBase _plugin; 99 | private HashSet _registeredNames; 100 | 101 | public Registrations(PluginBase plugin, HashSet registeredNames) 102 | { 103 | _plugin = plugin; 104 | _registeredNames = registeredNames; 105 | } 106 | 107 | public void Dispose() 108 | { 109 | var plugin = Interlocked.Exchange(ref _plugin, null); 110 | 111 | if (plugin != null) 112 | { 113 | foreach (var name in _registeredNames) 114 | { 115 | if (!Plugins._plugin_unregistercommand(plugin.PluginHandle, name)) 116 | PluginBase.LogError($"Unregistration of command '{name}' failed."); 117 | } 118 | 119 | _registeredNames = null; 120 | } 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/NativeBindings/Win32/Types.DebugEvents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DotNetPlugin.NativeBindings.Win32 5 | { 6 | #pragma warning disable 0649 7 | 8 | [Serializable] 9 | public unsafe struct EXCEPTION_RECORD 10 | { 11 | public const int EXCEPTION_MAXIMUM_PARAMETERS = 15; 12 | 13 | public uint ExceptionCode; 14 | public uint ExceptionFlags; 15 | 16 | private IntPtr ExceptionRecordPtr; 17 | public StructRef ExceptionRecord => new StructRef(ExceptionRecordPtr); 18 | 19 | public IntPtr ExceptionAddress; 20 | public uint NumberParameters; 21 | 22 | #if AMD64 23 | private fixed ulong ExceptionInformationFixed[EXCEPTION_MAXIMUM_PARAMETERS]; 24 | #else 25 | private fixed uint ExceptionInformationFixed[EXCEPTION_MAXIMUM_PARAMETERS]; 26 | #endif 27 | 28 | public UIntPtr[] GetExceptionInformation(UIntPtr[] array) 29 | { 30 | if (array == null) 31 | array = new UIntPtr[EXCEPTION_MAXIMUM_PARAMETERS]; 32 | 33 | #if AMD64 34 | fixed (ulong* ptr = ExceptionInformationFixed) 35 | #else 36 | fixed (uint* ptr = ExceptionInformationFixed) 37 | #endif 38 | { 39 | var p = ptr; 40 | for (int i = 0, n = Math.Min(array.Length, EXCEPTION_MAXIMUM_PARAMETERS); i < n; i++, p++) 41 | array[i] = new UIntPtr(*p); 42 | } 43 | 44 | return array; 45 | } 46 | } 47 | 48 | [Serializable] 49 | public struct EXCEPTION_DEBUG_INFO 50 | { 51 | public EXCEPTION_RECORD ExceptionRecord; 52 | public uint dwFirstChance; 53 | } 54 | 55 | [Serializable] 56 | public struct CREATE_THREAD_DEBUG_INFO 57 | { 58 | public IntPtr hThread; 59 | public IntPtr lpThreadLocalBase; 60 | public IntPtr lpStartAddress; // PTHREAD_START_ROUTINE 61 | } 62 | 63 | [Serializable] 64 | public struct CREATE_PROCESS_DEBUG_INFO 65 | { 66 | public IntPtr hFile; 67 | public IntPtr hProcess; 68 | public IntPtr hThread; 69 | public IntPtr lpBaseOfImage; 70 | public uint dwDebugInfoFileOffset; 71 | public uint nDebugInfoSize; 72 | public IntPtr lpThreadLocalBase; 73 | public IntPtr lpStartAddress; //PTHREAD_START_ROUTINE 74 | public IntPtr lpImageName; 75 | public ushort fUnicode; 76 | } 77 | 78 | [Serializable] 79 | public struct EXIT_THREAD_DEBUG_INFO 80 | { 81 | public uint dwExitCode; 82 | } 83 | 84 | [Serializable] 85 | public struct EXIT_PROCESS_DEBUG_INFO 86 | { 87 | public uint dwExitCode; 88 | } 89 | 90 | [Serializable] 91 | public struct LOAD_DLL_DEBUG_INFO 92 | { 93 | public IntPtr hFile; 94 | public IntPtr lpBaseOfDll; 95 | public uint dwDebugInfoFileOffset; 96 | public uint nDebugInfoSize; 97 | public IntPtr lpImageName; 98 | public ushort fUnicode; 99 | } 100 | 101 | [Serializable] 102 | public struct UNLOAD_DLL_DEBUG_INFO 103 | { 104 | public IntPtr lpBaseOfDll; 105 | } 106 | 107 | [Serializable] 108 | public struct OUTPUT_DEBUG_STRING_INFO 109 | { 110 | public IntPtr lpDebugStringData; 111 | public ushort fUnicode; 112 | public ushort nDebugStringLength; 113 | } 114 | 115 | [Serializable] 116 | public struct RIP_INFO 117 | { 118 | public uint dwError; 119 | public uint dwType; 120 | } 121 | 122 | public enum DebugEventType : uint 123 | { 124 | EXCEPTION_DEBUG_EVENT = 1, 125 | CREATE_THREAD_DEBUG_EVENT = 2, 126 | CREATE_PROCESS_DEBUG_EVENT = 3, 127 | EXIT_THREAD_DEBUG_EVENT = 4, 128 | EXIT_PROCESS_DEBUG_EVENT = 5, 129 | LOAD_DLL_DEBUG_EVENT = 6, 130 | UNLOAD_DLL_DEBUG_EVENT = 7, 131 | OUTPUT_DEBUG_STRING_EVENT = 8, 132 | RIP_EVENT = 9, 133 | } 134 | 135 | [Serializable] 136 | [StructLayout(LayoutKind.Explicit)] 137 | public struct DEBUG_EVENT_UNION 138 | { 139 | [FieldOffset(0)] public EXCEPTION_DEBUG_INFO Exception; 140 | [FieldOffset(0)] public CREATE_THREAD_DEBUG_INFO CreateThread; 141 | [FieldOffset(0)] public CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; 142 | [FieldOffset(0)] public EXIT_THREAD_DEBUG_INFO ExitThread; 143 | [FieldOffset(0)] public EXIT_PROCESS_DEBUG_INFO ExitProcess; 144 | [FieldOffset(0)] public LOAD_DLL_DEBUG_INFO LoadDll; 145 | [FieldOffset(0)] public UNLOAD_DLL_DEBUG_INFO UnloadDll; 146 | [FieldOffset(0)] public OUTPUT_DEBUG_STRING_INFO DebugString; 147 | [FieldOffset(0)] public RIP_INFO RipInfo; 148 | } 149 | 150 | [Serializable] 151 | public struct DEBUG_EVENT 152 | { 153 | public DebugEventType dwDebugEventCode; 154 | public int dwProcessId; 155 | public int dwThreadId; 156 | 157 | public DEBUG_EVENT_UNION u; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/Plugin.EventCallbacks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using DotNetPlugin.NativeBindings; 6 | using DotNetPlugin.NativeBindings.SDK; 7 | using DotNetPlugin.NativeBindings.Win32; 8 | 9 | namespace DotNetPlugin 10 | { 11 | partial class Plugin 12 | { 13 | 14 | [EventCallback(Plugins.CBTYPE.CB_INITDEBUG)] 15 | public static void OnInitDebug(ref Plugins.PLUG_CB_INITDEBUG info) 16 | { 17 | var szFileName = info.szFileName.GetValue(); 18 | LogInfo($"debugging of file {szFileName} started!"); 19 | GSimpleMcpServer.IsActivelyDebugging = true; 20 | } 21 | 22 | [EventCallback(Plugins.CBTYPE.CB_STOPDEBUG)] 23 | public static void OnStopDebug(ref Plugins.PLUG_CB_STOPDEBUG info) 24 | { 25 | LogInfo($"debugging stopped!"); 26 | GSimpleMcpServer.IsActivelyDebugging = false; 27 | } 28 | 29 | [EventCallback(Plugins.CBTYPE.CB_CREATEPROCESS)] 30 | public static void OnCreateProcess(IntPtr infoPtr) 31 | { 32 | // info can also be cast manually 33 | var info = infoPtr.ToStructUnsafe(); 34 | 35 | var CreateProcessInfo = info.CreateProcessInfo; 36 | var modInfo = info.modInfo; 37 | string DebugFileName = info.DebugFileName.GetValue(); 38 | var fdProcessInfo = info.fdProcessInfo; 39 | LogInfo($"Create process {DebugFileName}"); 40 | } 41 | 42 | [EventCallback(Plugins.CBTYPE.CB_LOADDLL)] 43 | public static void OnLoadDll(ref Plugins.PLUG_CB_LOADDLL info) 44 | { 45 | var LoadDll = info.LoadDll; 46 | var modInfo = info.modInfo; 47 | string modname = info.modname.GetValue(); 48 | LogInfo($"Load DLL {modname}"); 49 | } 50 | 51 | [EventCallback(Plugins.CBTYPE.CB_DEBUGEVENT)] 52 | public static void DebugEvent(ref Plugins.PLUG_CB_DEBUGEVENT info) 53 | { 54 | // *** Replace 'PointerToTheStringField' with the actual field name *** 55 | //Debug.WriteLine(info.DebugEvent.Value.dwDebugEventCode.ToString()); 56 | /* 57 | CREATE_THREAD_DEBUG_EVENT 58 | LOAD_DLL_DEBUG_EVENT 59 | EXCEPTION_DEBUG_EVENT 60 | EXIT_THREAD_DEBUG_EVENT 61 | EXIT_PROCESS_DEBUG_EVENT 62 | CREATE_PROCESS_DEBUG_EVENT 63 | */ 64 | 65 | if (info.DebugEvent.Value.dwDebugEventCode == DebugEventType.OUTPUT_DEBUG_STRING_EVENT)//DebugEventCode.OUTPUT_DEBUG_STRING_EVENT 66 | { 67 | IntPtr stringPointer = info.DebugEvent.Value.u.DebugString.lpDebugStringData; 68 | if (stringPointer != IntPtr.Zero) 69 | { 70 | try 71 | { 72 | if (info.DebugEvent.Value.u.DebugString.fUnicode != 0) // Non-zero means Unicode (UTF-16) 73 | { 74 | // Reads until the first null character (\0\0 for UTF-16) 75 | LogInfo(Marshal.PtrToStringUni(stringPointer) ?? string.Empty); 76 | } 77 | else // Zero means ANSI 78 | { 79 | // Reads until the first null character (\0) 80 | LogInfo(Marshal.PtrToStringAnsi(stringPointer) ?? string.Empty); 81 | } 82 | } 83 | catch (AccessViolationException accEx) 84 | { 85 | LogInfo($"Error: Access Violation trying to read string from pointer {stringPointer}. Check if pointer is valid. {accEx.Message}"); 86 | } 87 | catch (Exception ex) 88 | { 89 | LogInfo($"Error marshalling string from pointer {stringPointer}: {ex.Message}"); 90 | } 91 | } 92 | else 93 | { 94 | LogInfo("The relevant string pointer in PLUG_CB_DEBUGEVENT is null (IntPtr.Zero)."); 95 | } 96 | } 97 | // You can add more processing for other parts of the 'info' struct here 98 | } 99 | 100 | [EventCallback(Plugins.CBTYPE.CB_OUTPUTDEBUGSTRING)] 101 | public static void OutputDebugString(ref Plugins.PLUG_CB_OUTPUTDEBUGSTRING info) 102 | { 103 | LogInfo($"OutputDebugString "); 104 | } 105 | 106 | [EventCallback(Plugins.CBTYPE.CB_BREAKPOINT)] 107 | public static void Breakpoint(ref Plugins.PLUG_CB_BREAKPOINT info) 108 | { 109 | LogInfo($"Breakpoint " + info.breakpoint.Value.addr.ToHexString() + " in " + info.breakpoint.Value.mod); 110 | } 111 | 112 | [EventCallback(Plugins.CBTYPE.CB_SYSTEMBREAKPOINT)] 113 | public static void SystemBreakpoint(ref Plugins.PLUG_CB_SYSTEMBREAKPOINT info) 114 | { 115 | LogInfo($"SystemBreakpoint " + info.reserved); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/ExpressionFunctions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | using System.Threading; 9 | using DotNetPlugin.NativeBindings.SDK; 10 | 11 | namespace DotNetPlugin 12 | { 13 | /// 14 | /// Attribute for automatically registering expression functions in x64Dbg. 15 | /// 16 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] 17 | public class ExpressionFunctionAttribute : Attribute 18 | { 19 | public string Name { get; } 20 | 21 | public ExpressionFunctionAttribute() { } 22 | 23 | public ExpressionFunctionAttribute(string name) 24 | { 25 | Name = name; 26 | } 27 | } 28 | 29 | internal static class ExpressionFunctions 30 | { 31 | private static readonly MethodInfo s_marshalReadIntPtrMethod = new Func(Marshal.ReadIntPtr).Method; 32 | private static readonly MethodInfo s_intPtrToUIntPtrMethod = new Func(IntPtrToUIntPtr).Method; 33 | 34 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 35 | private static UIntPtr IntPtrToUIntPtr(IntPtr value) => (nuint)(nint)value; 36 | 37 | private static Plugins.CBPLUGINEXPRFUNCTION_RAWARGS BuildCallback(PluginBase plugin, MethodInfo method, int methodParamCount) 38 | { 39 | var argcParam = Expression.Parameter(typeof(int)); 40 | var argvParam = Expression.Parameter(typeof(IntPtr)); 41 | var userdataParam = Expression.Parameter(typeof(object)); 42 | 43 | var callArgs = Enumerable.Range(0, methodParamCount) 44 | .Select((param, i) => Expression.Call( 45 | s_intPtrToUIntPtrMethod, 46 | Expression.Call(s_marshalReadIntPtrMethod, argvParam, Expression.Constant(i * IntPtr.Size)))) 47 | .ToArray(); 48 | 49 | var call = method.IsStatic ? Expression.Call(method, callArgs) : Expression.Call(Expression.Constant(plugin), method, callArgs); 50 | 51 | var lambda = Expression.Lambda(call, argcParam, argvParam, userdataParam); 52 | 53 | return lambda.Compile(); 54 | } 55 | 56 | public static IDisposable Initialize(PluginBase plugin, MethodInfo[] pluginMethods) 57 | { 58 | // expression function names are case-sensitive 59 | var registeredNames = new HashSet(); 60 | 61 | var methods = pluginMethods 62 | .SelectMany(method => method.GetCustomAttributes().Select(attribute => (method, attribute))); 63 | 64 | foreach (var (method, attribute) in methods) 65 | { 66 | var name = attribute.Name ?? method.Name; 67 | 68 | if (method.ReturnType != typeof(UIntPtr)) 69 | { 70 | PluginBase.LogError($"Registration of expression function '{name}' is skipped. Method '{method.Name}' has an invalid return type."); 71 | continue; 72 | } 73 | 74 | var methodParams = method.GetParameters(); 75 | if (methodParams.Any(param => param.ParameterType != typeof(UIntPtr))) 76 | { 77 | PluginBase.LogError($"Registration of expression function '{name}' is skipped. Method '{method.Name}' has an invalid signature."); 78 | continue; 79 | } 80 | 81 | if (registeredNames.Contains(name) || 82 | !Plugins._plugin_registerexprfunction(plugin.PluginHandle, name, methodParams.Length, BuildCallback(plugin, method, methodParams.Length), null)) 83 | { 84 | PluginBase.LogError($"Registration of expression function '{name}' failed."); 85 | continue; 86 | } 87 | 88 | registeredNames.Add(name); 89 | } 90 | 91 | return new Registrations(plugin, registeredNames); 92 | } 93 | 94 | private sealed class Registrations : IDisposable 95 | { 96 | private PluginBase _plugin; 97 | private HashSet _registeredNames; 98 | 99 | public Registrations(PluginBase plugin, HashSet registeredNames) 100 | { 101 | _plugin = plugin; 102 | _registeredNames = registeredNames; 103 | } 104 | 105 | public void Dispose() 106 | { 107 | var plugin = Interlocked.Exchange(ref _plugin, null); 108 | 109 | if (plugin != null) 110 | { 111 | foreach (var name in _registeredNames) 112 | { 113 | if (!Plugins._plugin_unregisterexprfunction(plugin.PluginHandle, name)) 114 | PluginBase.LogError($"Unregistration of expression function '{name}' failed."); 115 | } 116 | 117 | _registeredNames = null; 118 | } 119 | } 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /DotNetPlugin.Stub/NativeBindings/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace DotNetPlugin.NativeBindings 8 | { 9 | public static class Extensions 10 | { 11 | static Extensions() 12 | { 13 | #if AMD64 14 | Debug.Assert(IntPtr.Size == 8); 15 | toPtrStringFormat = "X16"; 16 | #else 17 | Debug.Assert(IntPtr.Size == 4); 18 | toPtrStringFormat = "X8"; 19 | #endif 20 | } 21 | 22 | private static readonly string toPtrStringFormat; 23 | 24 | public static string ToHexString(this IntPtr intPtr) => 25 | intPtr.ToString("X"); 26 | 27 | public static string ToHexString(this UIntPtr intPtr) => 28 | ((nint)(nuint)intPtr).ToHexString(); 29 | 30 | public static string ToPtrString(this IntPtr intPtr) => 31 | intPtr.ToString(toPtrStringFormat); 32 | 33 | public static string ToPtrString(this UIntPtr intPtr) => 34 | ((nint)(nuint)intPtr).ToPtrString(); 35 | 36 | private static unsafe long GetCStrLength(byte* ptr) 37 | { 38 | byte* endPtr = ptr; 39 | 40 | for (; *endPtr != 0; endPtr++) { } 41 | 42 | return endPtr - ptr; 43 | } 44 | 45 | private static unsafe long GetCStrLength(byte* ptr, int size) 46 | { 47 | byte* endPtr = ptr; 48 | 49 | for (; size > 0 && *endPtr != 0; size--, endPtr++) { } 50 | 51 | return endPtr - ptr; 52 | } 53 | 54 | public static unsafe string MarshalToStringUTF8(this IntPtr buffer, int bufferSize) 55 | { 56 | if (bufferSize <= 0) 57 | throw new ArgumentOutOfRangeException(nameof(bufferSize)); 58 | 59 | if (buffer == IntPtr.Zero) 60 | return null; 61 | 62 | var bufferPtr = (byte*)buffer.ToPointer(); 63 | var length = checked((int)GetCStrLength(bufferPtr, bufferSize)); 64 | 65 | return Encoding.UTF8.GetString(bufferPtr, length); 66 | } 67 | 68 | public static unsafe void MarshalToPtrUTF8(this string str, IntPtr buffer, int bufferSize) 69 | { 70 | if (str == null) 71 | throw new ArgumentNullException(nameof(str)); 72 | 73 | if (bufferSize <= 0) 74 | throw new ArgumentOutOfRangeException(nameof(bufferSize)); 75 | 76 | fixed (char* strPtr = str) 77 | { 78 | var bufferPtr = (byte*)buffer.ToPointer(); 79 | var n = Encoding.UTF8.GetBytes(strPtr, str.Length, bufferPtr, bufferSize - 1); 80 | // makes sure that buffer contains a null terminated string 81 | *(bufferPtr + n) = 0; 82 | } 83 | } 84 | 85 | public static unsafe string MarshalToStringUTF8(this IntPtr buffer) 86 | { 87 | if (buffer == IntPtr.Zero) 88 | return null; 89 | 90 | // without unsafe it'd look like this... 91 | 92 | //using (var bytes = new MemoryStream()) 93 | //{ 94 | // byte @byte; 95 | // for (int i = 0; (@byte = Marshal.ReadByte(intPtr, i)) != 0; i++) 96 | // bytes.WriteByte(@byte); 97 | 98 | // return Encoding.UTF8.GetString(bytes.GetBuffer(), 0, (int)bytes.Position); 99 | //} 100 | 101 | // ...but here we want as few extra allocations and copying as possible 102 | 103 | var bufferPtr = (byte*)buffer.ToPointer(); 104 | var length = checked((int)GetCStrLength(bufferPtr)); 105 | 106 | return Encoding.UTF8.GetString(bufferPtr, length); 107 | } 108 | 109 | public static string[] MarshalToStringUTF8(this IntPtr[] intPtrs) 110 | { 111 | var strings = new string[intPtrs.Length]; 112 | 113 | for (int i = 0; i < intPtrs.Length; i++) 114 | strings[i] = intPtrs[i].MarshalToStringUTF8(); 115 | 116 | return strings; 117 | } 118 | 119 | public static T? ToStruct(this IntPtr intPtr) where T : struct 120 | { 121 | if (intPtr == IntPtr.Zero) 122 | return null; 123 | 124 | return (T)Marshal.PtrToStructure(intPtr, typeof(T)); 125 | } 126 | 127 | /// 128 | /// Safe to use with blittable types only! 129 | /// 130 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 131 | public static unsafe ref T ToStructUnsafe(this IntPtr intPtr) where T : unmanaged 132 | { 133 | if (intPtr == IntPtr.Zero) 134 | ThrowInvalidPointerException(intPtr); 135 | 136 | return ref *(T*)intPtr.ToPointer(); 137 | 138 | static void ThrowInvalidPointerException(IntPtr intPtr) 139 | { 140 | throw new ArgumentException("Invalid pointer.", nameof(intPtr)); 141 | } 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/PluginSessionProxy.cs: -------------------------------------------------------------------------------- 1 | #if ALLOW_UNLOADING 2 | 3 | using System; 4 | using System.IO; 5 | using System.Runtime.CompilerServices; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using DotNetPlugin.NativeBindings.SDK; 9 | 10 | namespace DotNetPlugin 11 | { 12 | /// 13 | /// A proxy for . (Supports Impl assembly unloading.) 14 | /// Creates a session in a separate app domain and forward calls to it, thus, enables the Impl assembly 15 | /// to be unloaded, replaced and reloaded without restarting the host application. 16 | /// 17 | /// 18 | /// We need this because x64dbg's default plugin unloading won't work in the case of .NET libraries. 19 | /// 20 | internal sealed class PluginSessionProxy : IPluginSession 21 | { 22 | private readonly AppDomain _appDomain; 23 | 24 | private readonly WaitCallback _implChangedCallback; 25 | private readonly CancellationTokenSource _implChangeWatcherCts; 26 | private volatile Task _watchForImplChangeTask; 27 | 28 | private volatile PluginSession _session; 29 | 30 | public PluginSessionProxy(WaitCallback implChangedCallback) 31 | { 32 | var appDomainSetup = new AppDomainSetup 33 | { 34 | ApplicationBase = Path.GetDirectoryName(typeof(PluginMain).Assembly.Location), 35 | AppDomainInitializer = AppDomainInitializer.Initialize 36 | }; 37 | 38 | _appDomain = AppDomain.CreateDomain("PluginImplDomain", null, appDomainSetup); 39 | 40 | _session = (PluginSession)_appDomain.CreateInstanceAndUnwrap(typeof(PluginSession).Assembly.GetName().Name, typeof(PluginSession).FullName); 41 | 42 | _implChangedCallback = implChangedCallback; 43 | _implChangeWatcherCts = new CancellationTokenSource(); 44 | _watchForImplChangeTask = Task.CompletedTask; 45 | } 46 | 47 | public void Dispose() => Stop(); 48 | 49 | public int PluginHandle 50 | { 51 | get => _session.PluginHandle; 52 | set => _session.PluginHandle = value; 53 | } 54 | 55 | private async Task WatchForImplChangeAsync() 56 | { 57 | if (PluginMain.ImplAssemblyLocation == null) 58 | return; 59 | 60 | RestartWatch: 61 | 62 | FileSystemWatcher fsw; 63 | 64 | try { fsw = new FileSystemWatcher(Path.GetDirectoryName(PluginMain.ImplAssemblyLocation), Path.GetFileName(PluginMain.ImplAssemblyLocation)); } 65 | catch 66 | { 67 | await Task.Delay(1000, _implChangeWatcherCts.Token).ConfigureAwait(false); 68 | goto RestartWatch; 69 | } 70 | 71 | using (fsw) 72 | { 73 | var changedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); 74 | 75 | fsw.Created += delegate { changedTcs.TrySetResult(null); }; 76 | fsw.Changed += delegate { changedTcs.TrySetResult(null); }; 77 | fsw.Renamed += delegate { changedTcs.TrySetResult(null); }; 78 | fsw.Deleted += delegate { changedTcs.TrySetResult(null); }; 79 | 80 | fsw.Error += (_, e) => changedTcs.TrySetException(e.GetException()); 81 | 82 | _implChangeWatcherCts.Token.Register(() => changedTcs.TrySetCanceled(_implChangeWatcherCts.Token)); 83 | 84 | fsw.EnableRaisingEvents = true; 85 | 86 | try { await changedTcs.Task.ConfigureAwait(false); } 87 | catch (Exception ex) when (!(ex is OperationCanceledException)) 88 | { 89 | await Task.Delay(1000, _implChangeWatcherCts.Token).ConfigureAwait(false); 90 | goto RestartWatch; 91 | } 92 | } 93 | 94 | await Task.Delay(500, _implChangeWatcherCts.Token).ConfigureAwait(false); 95 | 96 | RetryFileAccess: 97 | 98 | try 99 | { 100 | if (File.Exists(PluginMain.ImplAssemblyLocation)) 101 | File.OpenRead(PluginMain.ImplAssemblyLocation).Dispose(); 102 | } 103 | catch 104 | { 105 | await Task.Delay(1000, _implChangeWatcherCts.Token).ConfigureAwait(false); 106 | goto RetryFileAccess; 107 | } 108 | 109 | ThreadPool.QueueUserWorkItem(_implChangedCallback, this); 110 | } 111 | 112 | public bool Init() 113 | { 114 | var result = _session.Init(); 115 | 116 | if (result) 117 | { 118 | _watchForImplChangeTask = WatchForImplChangeAsync(); 119 | } 120 | 121 | return result; 122 | } 123 | 124 | public void Setup(ref Plugins.PLUG_SETUPSTRUCT setupStruct) => _session.Setup(ref setupStruct); 125 | 126 | private bool StopCore(PluginSession session) 127 | { 128 | try 129 | { 130 | _implChangeWatcherCts.Cancel(); 131 | 132 | var watchForImplChangeTask = Interlocked.Exchange(ref _watchForImplChangeTask, Task.CompletedTask); 133 | var sessionStopTask = Task.Factory.StartNew(session.Stop, TaskCreationOptions.LongRunning); 134 | 135 | var pendingTasks = Task.WhenAll(watchForImplChangeTask, sessionStopTask); 136 | 137 | if (Task.WhenAny(pendingTasks, Task.Delay(5000)).ConfigureAwait(false).GetAwaiter().GetResult() == pendingTasks) 138 | { 139 | if (pendingTasks.IsFaulted) 140 | pendingTasks.ConfigureAwait(false).GetAwaiter().GetResult(); // unwraps exception 141 | else 142 | return sessionStopTask.ConfigureAwait(false).GetAwaiter().GetResult(); 143 | } 144 | } 145 | catch (Exception ex) 146 | { 147 | PluginMain.LogUnhandledException(ex); 148 | } 149 | 150 | return false; 151 | } 152 | 153 | public bool Stop() 154 | { 155 | var session = Interlocked.Exchange(ref _session, PluginSession.Null); 156 | 157 | if (session == PluginSession.Null) 158 | return true; 159 | 160 | var result = StopCore(session); 161 | 162 | _implChangeWatcherCts.Dispose(); 163 | 164 | AppDomain.Unload(_appDomain); 165 | 166 | return result; 167 | } 168 | 169 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 170 | public void OnMenuEntry(ref Plugins.PLUG_CB_MENUENTRY info) => _session.OnMenuEntry(ref info); 171 | } 172 | } 173 | 174 | #endif -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/Script/Module.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using DotNetPlugin.NativeBindings.SDK; 4 | using DotNetPlugin.NativeBindings.Win32; 5 | 6 | namespace DotNetPlugin.NativeBindings.Script 7 | { 8 | // https://github.com/x64dbg/x64dbg/blob/development/src/dbg/_scriptapi_module.h 9 | public static class Module 10 | { 11 | [Serializable] 12 | public unsafe struct ModuleInfo 13 | { 14 | public nuint @base; 15 | public nuint size; 16 | public nuint entry; 17 | public int sectionCount; 18 | 19 | private fixed byte nameBytes[Bridge.MAX_MODULE_SIZE]; 20 | public string name 21 | { 22 | get 23 | { 24 | fixed (byte* ptr = nameBytes) 25 | return new IntPtr(ptr).MarshalToStringUTF8(Bridge.MAX_MODULE_SIZE); 26 | } 27 | } 28 | 29 | private fixed byte pathBytes[Win32Constants.MAX_PATH]; 30 | public string path 31 | { 32 | get 33 | { 34 | fixed (byte* ptr = pathBytes) 35 | return new IntPtr(ptr).MarshalToStringUTF8(Win32Constants.MAX_PATH); 36 | } 37 | } 38 | } 39 | 40 | [Serializable] 41 | public unsafe struct ModuleSectionInfo 42 | { 43 | public nuint addr; 44 | public nuint size; 45 | 46 | private fixed byte nameBytes[Bridge.MAX_SECTION_SIZE * 5]; 47 | public string name 48 | { 49 | get 50 | { 51 | fixed (byte* ptr = nameBytes) 52 | return new IntPtr(ptr).MarshalToStringUTF8(Bridge.MAX_SECTION_SIZE * 5); 53 | } 54 | } 55 | } 56 | 57 | #if AMD64 58 | private const string dll = "x64dbg.dll"; 59 | 60 | private const string Script_Module_GetListEP = "?GetList@Module@Script@@YA_NPEAUListInfo@@@Z"; 61 | private const string Script_Module_SectionListFromAddrEP = "?SectionListFromAddr@Module@Script@@YA_N_KPEAUListInfo@@@Z"; 62 | private const string Script_Module_InfoFromAddrEP = "?InfoFromAddr@Module@Script@@YA_N_KPEAUModuleInfo@12@@Z"; 63 | private const string Script_Module_NameFromAddrEP = "?NameFromAddr@Module@Script@@YA_N_KPEAD@Z"; 64 | private const string Script_Module_BaseFromAddrEP = "?BaseFromAddr@Module@Script@@YA_K_K@Z"; 65 | private const string Script_Module_EntryFromAddrEP = "?EntryFromAddr@Module@Script@@YA_K_K@Z"; 66 | private const string Script_Module_SectionFromNameEP = "?SectionFromName@Module@Script@@YA_NPEBDHPEAUModuleSectionInfo@12@@Z"; 67 | private const string Script_Module_GetMainModuleInfoEP = "?GetMainModuleInfo@Module@Script@@YA_NPEAUModuleInfo@12@@Z"; 68 | private const string Script_Module_GetMainModuleNameEP = "?GetMainModuleName@Module@Script@@YA_NPEAD@Z"; 69 | #else 70 | private const string dll = "x32dbg.dll"; 71 | 72 | private const string Script_Module_GetListEP = "?GetList@Module@Script@@YA_NPAUListInfo@@@Z"; 73 | private const string Script_Module_SectionListFromAddrEP = "?SectionListFromAddr@Module@Script@@YA_NKPAUListInfo@@@Z"; 74 | private const string Script_Module_InfoFromAddrEP = "?InfoFromAddr@Module@Script@@YA_NKPAUModuleInfo@12@@Z"; 75 | private const string Script_Module_NameFromAddrEP = "?NameFromAddr@Module@Script@@YA_NKPAD@Z"; 76 | private const string Script_Module_BaseFromAddrEP = "?BaseFromAddr@Module@Script@@YAKK@Z"; 77 | private const string Script_Module_EntryFromAddrEP = "?EntryFromAddr@Module@Script@@YAKK@Z"; 78 | private const string Script_Module_SectionFromNameEP = "?SectionFromName@Module@Script@@YA_NPBDHPAUModuleSectionInfo@12@@Z"; 79 | private const string Script_Module_GetMainModuleInfoEP = "?GetMainModuleInfo@Module@Script@@YA_NPAUModuleInfo@12@@Z"; 80 | private const string Script_Module_GetMainModuleNameEP = "?GetMainModuleName@Module@Script@@YA_NPAD@Z"; 81 | #endif 82 | private const CallingConvention cdecl = CallingConvention.Cdecl; 83 | 84 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Module_GetListEP, ExactSpelling = true)] 85 | private static extern bool Script_Module_GetList(ref Bridge.ListInfo listInfo); 86 | 87 | public static ModuleInfo[] GetList() 88 | { 89 | var listInfo = new Bridge.ListInfo(); 90 | return listInfo.ToArray(Script_Module_GetList(ref listInfo)); 91 | } 92 | 93 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Module_SectionListFromAddrEP, ExactSpelling = true)] 94 | private static extern bool Script_Module_SectionListFromAddr(nuint addr, ref Bridge.ListInfo listInfo); 95 | 96 | public static ModuleSectionInfo[] SectionListFromAddr(nuint addr) 97 | { 98 | var listInfo = new Bridge.ListInfo(); 99 | return listInfo.ToArray(Script_Module_SectionListFromAddr(addr, ref listInfo)); 100 | } 101 | 102 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Module_InfoFromAddrEP, ExactSpelling = true)] 103 | private static extern bool Script_Module_InfoFromAddr(nuint addr, ref ModuleInfo info); 104 | 105 | public static bool InfoFromAddr(nuint addr, ref ModuleInfo info) 106 | { 107 | return Script_Module_InfoFromAddr(addr, ref info); 108 | } 109 | 110 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Module_NameFromAddrEP, ExactSpelling = true)] 111 | private static extern bool Script_Module_NameFromAddr(nuint addr, IntPtr name); 112 | 113 | public static bool NameFromAddr(nuint addr, out string name) 114 | { 115 | var nameBuffer = Marshal.AllocHGlobal(Bridge.MAX_MODULE_SIZE); 116 | try 117 | { 118 | var success = Script_Module_NameFromAddr(addr, nameBuffer); 119 | name = success ? nameBuffer.MarshalToStringUTF8(Bridge.MAX_MODULE_SIZE) : default; 120 | return success; 121 | } 122 | finally 123 | { 124 | Marshal.FreeHGlobal(nameBuffer); 125 | } 126 | } 127 | 128 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Module_BaseFromAddrEP, ExactSpelling = true)] 129 | private static extern nuint Script_Module_BaseFromAddr(nuint addr); 130 | 131 | public static nuint BaseFromAddr(nuint addr) => Script_Module_BaseFromAddr(addr); 132 | 133 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Module_EntryFromAddrEP, ExactSpelling = true)] 134 | private static extern nuint Script_Module_EntryFromAddr(nuint addr); 135 | 136 | public static nuint EntryFromAddr(nuint addr) => Script_Module_EntryFromAddr(addr); 137 | 138 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Module_SectionFromNameEP, ExactSpelling = true)] 139 | private static extern bool Script_Module_SectionFromName( 140 | [MarshalAs(UnmanagedType.LPUTF8Str)] string name, 141 | int number, 142 | ref ModuleSectionInfo section); 143 | 144 | public static bool SectionFromName(string name, int number, ref ModuleSectionInfo section) => 145 | Script_Module_SectionFromName(name, number, ref section); 146 | 147 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Module_GetMainModuleInfoEP, ExactSpelling = true)] 148 | private static extern bool Script_Module_GetMainModuleInfo(ref ModuleInfo info); 149 | 150 | public static bool GetMainModuleInfo(ref ModuleInfo info) => Script_Module_GetMainModuleInfo(ref info); 151 | 152 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = Script_Module_GetMainModuleNameEP, ExactSpelling = true)] 153 | private static extern bool Script_Module_GetMainModuleName(IntPtr name); 154 | 155 | public static bool GetMainModuleName(out string name) 156 | { 157 | var nameBuffer = Marshal.AllocHGlobal(Bridge.MAX_MODULE_SIZE); 158 | try 159 | { 160 | var success = Script_Module_GetMainModuleName(nameBuffer); 161 | name = success ? nameBuffer.MarshalToStringUTF8(Bridge.MAX_MODULE_SIZE) : default; 162 | return success; 163 | } 164 | finally 165 | { 166 | Marshal.FreeHGlobal(nameBuffer); 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Nuget personal access tokens and Credentials 210 | # nuget.config 211 | 212 | # Microsoft Azure Build Output 213 | csx/ 214 | *.build.csdef 215 | 216 | # Microsoft Azure Emulator 217 | ecf/ 218 | rcf/ 219 | 220 | # Windows Store app package directories and files 221 | AppPackages/ 222 | BundleArtifacts/ 223 | Package.StoreAssociation.xml 224 | _pkginfo.txt 225 | *.appx 226 | *.appxbundle 227 | *.appxupload 228 | 229 | # Visual Studio cache files 230 | # files ending in .cache can be ignored 231 | *.[Cc]ache 232 | # but keep track of directories ending in .cache 233 | !?*.[Cc]ache/ 234 | 235 | # Others 236 | ClientBin/ 237 | ~$* 238 | *~ 239 | *.dbmdl 240 | *.dbproj.schemaview 241 | *.jfm 242 | *.pfx 243 | *.publishsettings 244 | orleans.codegen.cs 245 | 246 | # Including strong name files can present a security risk 247 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 248 | #*.snk 249 | 250 | # Since there are multiple workflows, uncomment next line to ignore bower_components 251 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 252 | #bower_components/ 253 | 254 | # RIA/Silverlight projects 255 | Generated_Code/ 256 | 257 | # Backup & report files from converting an old project file 258 | # to a newer Visual Studio version. Backup files are not needed, 259 | # because we have git ;-) 260 | _UpgradeReport_Files/ 261 | Backup*/ 262 | UpgradeLog*.XML 263 | UpgradeLog*.htm 264 | ServiceFabricBackup/ 265 | *.rptproj.bak 266 | 267 | # SQL Server files 268 | *.mdf 269 | *.ldf 270 | *.ndf 271 | 272 | # Business Intelligence projects 273 | *.rdl.data 274 | *.bim.layout 275 | *.bim_*.settings 276 | *.rptproj.rsuser 277 | *- [Bb]ackup.rdl 278 | *- [Bb]ackup ([0-9]).rdl 279 | *- [Bb]ackup ([0-9][0-9]).rdl 280 | 281 | # Microsoft Fakes 282 | FakesAssemblies/ 283 | 284 | # GhostDoc plugin setting file 285 | *.GhostDoc.xml 286 | 287 | # Node.js Tools for Visual Studio 288 | .ntvs_analysis.dat 289 | node_modules/ 290 | 291 | # Visual Studio 6 build log 292 | *.plg 293 | 294 | # Visual Studio 6 workspace options file 295 | *.opt 296 | 297 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 298 | *.vbw 299 | 300 | # Visual Studio LightSwitch build output 301 | **/*.HTMLClient/GeneratedArtifacts 302 | **/*.DesktopClient/GeneratedArtifacts 303 | **/*.DesktopClient/ModelManifest.xml 304 | **/*.Server/GeneratedArtifacts 305 | **/*.Server/ModelManifest.xml 306 | _Pvt_Extensions 307 | 308 | # Paket dependency manager 309 | .paket/paket.exe 310 | paket-files/ 311 | 312 | # FAKE - F# Make 313 | .fake/ 314 | 315 | # CodeRush personal settings 316 | .cr/personal 317 | 318 | # Python Tools for Visual Studio (PTVS) 319 | __pycache__/ 320 | *.pyc 321 | 322 | # Cake - Uncomment if you are using it 323 | # tools/** 324 | # !tools/packages.config 325 | 326 | # Tabs Studio 327 | *.tss 328 | 329 | # Telerik's JustMock configuration file 330 | *.jmconfig 331 | 332 | # BizTalk build output 333 | *.btp.cs 334 | *.btm.cs 335 | *.odx.cs 336 | *.xsd.cs 337 | 338 | # OpenCover UI analysis results 339 | OpenCover/ 340 | 341 | # Azure Stream Analytics local run output 342 | ASALocalRun/ 343 | 344 | # MSBuild Binary and Structured Log 345 | *.binlog 346 | 347 | # NVidia Nsight GPU debugger configuration file 348 | *.nvuser 349 | 350 | # MFractors (Xamarin productivity tool) working folder 351 | .mfractor/ 352 | 353 | # Local History for Visual Studio 354 | .localhistory/ 355 | 356 | # BeatPulse healthcheck temp database 357 | healthchecksdb 358 | 359 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 360 | MigrationBackup/ 361 | 362 | # Ionide (cross platform F# VS Code tools) working folder 363 | .ionide/ 364 | 365 | # Fody - auto-generated XML schema 366 | FodyWeavers.xsd 367 | 368 | # VS Code files for those working on multiple tools 369 | .vscode/* 370 | !.vscode/settings.json 371 | !.vscode/tasks.json 372 | !.vscode/launch.json 373 | !.vscode/extensions.json 374 | *.code-workspace 375 | 376 | # Local History for Visual Studio Code 377 | .history/ 378 | 379 | # Windows Installer files from build outputs 380 | *.cab 381 | *.msi 382 | *.msix 383 | *.msm 384 | *.msp 385 | 386 | # JetBrains Rider 387 | .idea/ 388 | *.sln.iml 389 | 390 | !DotNetPlugin.Impl/NativeBindings/Win32/ 391 | !DotNetPlugin.Stub/NativeBindings/Win32/ 392 | 393 | # CI and local build caches 394 | .cache/ 395 | **/.cache/ 396 | packages/ -------------------------------------------------------------------------------- /DotNetPlugin.Stub/NativeBindings/SDK/Bridge.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Drawing.Imaging; 4 | using System.IO; 5 | using System.Runtime.InteropServices; 6 | using DotNetPlugin.NativeBindings.Win32; 7 | 8 | namespace DotNetPlugin.NativeBindings.SDK 9 | { 10 | // https://github.com/x64dbg/x64dbg/blob/development/src/bridge/bridgemain.h 11 | public class BridgeBase 12 | { 13 | public const int MAX_LABEL_SIZE = 256; 14 | public const int MAX_COMMENT_SIZE = 512; 15 | public const int MAX_MODULE_SIZE = 256; 16 | public const int MAX_IMPORT_SIZE = 65536; 17 | public const int MAX_BREAKPOINT_SIZE = 256; 18 | public const int MAX_CONDITIONAL_EXPR_SIZE = 256; 19 | public const int MAX_CONDITIONAL_TEXT_SIZE = 256; 20 | public const int MAX_SCRIPT_LINE_SIZE = 2048; 21 | public const int MAX_THREAD_NAME_SIZE = 256; 22 | public const int MAX_WATCH_NAME_SIZE = 256; 23 | public const int MAX_STRING_SIZE = 512; 24 | public const int MAX_ERROR_SIZE = 512; 25 | public const int MAX_SECTION_SIZE = 10; 26 | public const int MAX_COMMAND_LINE_SIZE = 256; 27 | public const int MAX_MNEMONIC_SIZE = 64; 28 | public const int PAGE_SIZE = 4096; 29 | 30 | #if AMD64 31 | protected const string dll = "x64bridge.dll"; 32 | #else 33 | protected const string dll = "x32bridge.dll"; 34 | #endif 35 | protected const CallingConvention cdecl = CallingConvention.Cdecl; 36 | 37 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 38 | public static extern IntPtr BridgeAlloc(nuint size); 39 | 40 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 41 | public static extern void BridgeFree(IntPtr ptr); 42 | 43 | protected BridgeBase() { } 44 | 45 | #pragma warning disable 0649 46 | 47 | public enum GUIMENUTYPE 48 | { 49 | GUI_PLUGIN_MENU, 50 | GUI_DISASM_MENU, 51 | GUI_DUMP_MENU, 52 | GUI_STACK_MENU, 53 | GUI_GRAPH_MENU, 54 | GUI_MEMMAP_MENU, 55 | GUI_SYMMOD_MENU, 56 | } 57 | 58 | [Serializable] 59 | public struct BridgeCFGraphList 60 | { 61 | public nuint entryPoint; //graph entry point 62 | public IntPtr userdata; //user data 63 | public ListInfo nodes; //graph nodes (BridgeCFNodeList) 64 | } 65 | 66 | // https://github.com/x64dbg/x64dbg/blob/development/src/bridge/bridgelist.h 67 | [Serializable] 68 | public struct ListInfo 69 | { 70 | public int count; 71 | public nuint size; 72 | public IntPtr data; 73 | 74 | public T[] ToArray(bool success) where T : new() 75 | { 76 | if (!success || count == 0 || size == 0) 77 | return Array.Empty(); 78 | var list = new T[count]; 79 | var szt = Marshal.SizeOf(typeof(T)); 80 | var sz = checked((int)(size / (nuint)count)); 81 | if (szt != sz) 82 | throw new InvalidDataException(string.Format("{0} type size mismatch, expected {1} got {2}!", 83 | typeof(T).Name, szt, sz)); 84 | var ptr = data; 85 | for (var i = 0; i < count; i++) 86 | { 87 | list[i] = (T)Marshal.PtrToStructure(ptr, typeof(T)); 88 | ptr += sz; 89 | } 90 | BridgeFree(data); 91 | return list; 92 | } 93 | } 94 | 95 | [Serializable] 96 | public struct FUNCTION 97 | { 98 | public nuint start; //OUT 99 | public nuint end; //OUT 100 | public nuint instrcount; //OUT 101 | } 102 | 103 | [Serializable] 104 | public struct LOOP 105 | { 106 | public int depth; //IN 107 | public nuint start; //OUT 108 | public nuint end; //OUT 109 | public nuint instrcount; //OUT 110 | } 111 | 112 | [Serializable] 113 | public unsafe struct BRIDGE_ADDRINFO 114 | { 115 | public int flags; //ADDRINFOFLAGS (IN) 116 | 117 | private fixed byte moduleBytes[MAX_MODULE_SIZE]; //module the address is in 118 | public string module 119 | { 120 | get 121 | { 122 | fixed (byte* ptr = moduleBytes) 123 | return new IntPtr(ptr).MarshalToStringUTF8(MAX_MODULE_SIZE); 124 | } 125 | } 126 | 127 | private fixed byte labelBytes[MAX_LABEL_SIZE]; 128 | public string label 129 | { 130 | get 131 | { 132 | fixed (byte* ptr = labelBytes) 133 | return new IntPtr(ptr).MarshalToStringUTF8(MAX_LABEL_SIZE); 134 | } 135 | } 136 | 137 | private fixed byte commentBytes[MAX_COMMENT_SIZE]; 138 | public string comment 139 | { 140 | get 141 | { 142 | fixed (byte* ptr = commentBytes) 143 | return new IntPtr(ptr).MarshalToStringUTF8(MAX_COMMENT_SIZE); 144 | } 145 | } 146 | 147 | public BlittableBoolean isbookmark; 148 | public FUNCTION function; 149 | public LOOP loop; 150 | public FUNCTION args; 151 | } 152 | 153 | [Serializable] 154 | public struct ICONDATA 155 | { 156 | public IntPtr data; 157 | public nuint size; 158 | 159 | private static unsafe byte[] GetIconDataCore(Bitmap bitmap) 160 | { 161 | byte[] bitmapDataArray; 162 | 163 | const PixelFormat pixelFormat = PixelFormat.Format32bppArgb; 164 | var bitsPerPixel = Image.GetPixelFormatSize(pixelFormat); 165 | 166 | var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, pixelFormat); 167 | try 168 | { 169 | int pixelArraySize = bitmapData.Stride * bitmapData.Height; 170 | bitmapDataArray = new byte[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV5HEADER) + pixelArraySize]; 171 | 172 | fixed (byte* bitmapDataArrayPtr = bitmapDataArray) 173 | { 174 | byte* destPtr = bitmapDataArrayPtr; 175 | int destAvailableSize = bitmapDataArray.Length; 176 | 177 | ref BITMAPFILEHEADER bmfh = ref *(BITMAPFILEHEADER*)destPtr; 178 | bmfh.bfType = 0x4d42; 179 | bmfh.bfSize = (uint)bitmapDataArray.Length; 180 | bmfh.bfOffBits = (uint)(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV5HEADER)); 181 | 182 | destPtr += sizeof(BITMAPFILEHEADER); 183 | destAvailableSize -= sizeof(BITMAPFILEHEADER); 184 | 185 | ref BITMAPV5HEADER bmh = ref *(BITMAPV5HEADER*)destPtr; 186 | bmh.bV5Size = (uint)sizeof(BITMAPV5HEADER); 187 | bmh.bV5Width = bitmapData.Width; 188 | bmh.bV5Height = -bitmapData.Height; 189 | bmh.bV5Planes = 1; 190 | bmh.bV5BitCount = (ushort)bitsPerPixel; 191 | bmh.bV5Compression = BitmapCompressionMode.BI_RGB | BitmapCompressionMode.BI_BITFIELDS; 192 | bmh.bV5RedMask = 0xFFu << 16; 193 | bmh.bV5GreenMask = 0xFFu << 8; 194 | bmh.bV5BlueMask = 0xFFu; 195 | bmh.bV5AlphaMask = 0xFFu << 24; 196 | bmh.bV5SizeImage = (uint)pixelArraySize; 197 | bmh.bV5XPelsPerMeter = 0; 198 | bmh.bV5YPelsPerMeter = 0; 199 | bmh.bV5CSType = LCSCSTYPE.LCS_sRGB; 200 | bmh.bV5Intent = LCSGAMUTMATCH.LCS_GM_GRAPHICS; 201 | 202 | destPtr += sizeof(BITMAPV5HEADER); 203 | destAvailableSize -= sizeof(BITMAPV5HEADER); 204 | 205 | Buffer.MemoryCopy(bitmapData.Scan0.ToPointer(), destPtr, destAvailableSize, pixelArraySize); 206 | } 207 | } 208 | finally 209 | { 210 | bitmap.UnlockBits(bitmapData); 211 | } 212 | 213 | return bitmapDataArray; 214 | } 215 | 216 | public static byte[] GetIconData(Icon icon) 217 | { 218 | using var bitmap = icon.ToBitmap(); 219 | return GetIconDataCore(bitmap); 220 | } 221 | 222 | public static unsafe byte[] GetIconData(Image image) 223 | { 224 | using var bitmap = new Bitmap(image); 225 | return GetIconDataCore(bitmap); 226 | } 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/Menus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using DotNetPlugin.NativeBindings.SDK; 5 | 6 | namespace DotNetPlugin 7 | { 8 | /// 9 | /// Not thread-safe. If you want to modify the menu structure dynamically, you are responsible for synchronization. 10 | /// See also . 11 | /// 12 | public sealed class Menus : IDisposable 13 | { 14 | private int _lastId; 15 | internal Dictionary _menuItemsById; 16 | 17 | internal Menus(int pluginHandle, ref Plugins.PLUG_SETUPSTRUCT setupStruct) 18 | { 19 | PluginHandle = pluginHandle; 20 | 21 | All = new[] 22 | { 23 | Main = new Menu(this, setupStruct.hMenu), 24 | Disasm = new Menu(this, setupStruct.hMenuDisasm), 25 | Dump = new Menu(this, setupStruct.hMenuDump), 26 | Stack = new Menu(this, setupStruct.hMenuStack), 27 | Graph = new Menu(this, setupStruct.hMenuGraph), 28 | Memmap = new Menu(this, setupStruct.hMenuMemmap), 29 | Symmod = new Menu(this, setupStruct.hMenuSymmod), 30 | }; 31 | 32 | _menuItemsById = new Dictionary(); 33 | } 34 | 35 | public void Dispose() 36 | { 37 | if (_menuItemsById != null) 38 | { 39 | Clear(); 40 | _menuItemsById = null; 41 | } 42 | } 43 | 44 | internal void EnsureNotDisposed() 45 | { 46 | if (_menuItemsById == null) 47 | throw new ObjectDisposedException(nameof(Menus)); 48 | } 49 | 50 | internal int PluginHandle { get; } 51 | 52 | public Menu Main; // main menu 53 | public Menu Disasm; // disasm menu 54 | public Menu Dump; // dump menu 55 | public Menu Stack; // stack menu 56 | public Menu Graph; // graph menu 57 | public Menu Memmap; // memory map menu 58 | public Menu Symmod; // symbol module menu 59 | 60 | public IReadOnlyList All { get; } 61 | 62 | internal int NextItemId() => ++_lastId; 63 | 64 | internal MenuItem GetMenuItemById(int id) => _menuItemsById.TryGetValue(id, out var menuItem) ? menuItem : null; 65 | 66 | public void Clear() 67 | { 68 | foreach (var menu in All) 69 | menu.Clear(); 70 | } 71 | } 72 | 73 | public sealed class MenuException : ApplicationException 74 | { 75 | public MenuException(string message) : base(message) { } 76 | } 77 | 78 | public abstract class MenuItemBase 79 | { 80 | internal MenuItemBase(Menu parent) 81 | { 82 | Parent = parent; 83 | } 84 | 85 | public Menu Parent { get; } 86 | 87 | public abstract bool Remove(); 88 | } 89 | 90 | public sealed class Menu : MenuItemBase 91 | { 92 | internal readonly Menus _menus; 93 | 94 | private Menu(Menus menus, Menu parent, int handle) : base(parent) 95 | { 96 | _menus = menus; 97 | Handle = handle; 98 | 99 | _items = new List(); 100 | } 101 | 102 | internal Menu(Menus menus, int handle) : this(menus, null, handle) { } 103 | 104 | private Menu(Menu parent, int handle) : this(parent._menus, parent, handle) { } 105 | 106 | public int Handle { get; } 107 | public bool IsRoot => Parent == null; 108 | 109 | internal readonly List _items; 110 | public IReadOnlyList Items => _items; 111 | 112 | public Menu AddAndConfigureSubMenu(string title) 113 | { 114 | if (title == null) 115 | throw new ArgumentNullException(nameof(title)); 116 | 117 | _menus.EnsureNotDisposed(); 118 | 119 | var subMenuHandle = Plugins._plugin_menuadd(Handle, title); 120 | 121 | if (subMenuHandle < 0) 122 | throw new MenuException($"Failed to add sub-menu '{title}'."); 123 | 124 | var subMenu = new Menu(this, subMenuHandle); 125 | _items.Add(subMenu); 126 | return subMenu; 127 | } 128 | 129 | public Menu AddSubMenu(string title) 130 | { 131 | AddAndConfigureSubMenu(title); 132 | return this; 133 | } 134 | 135 | public MenuItem AddAndConfigureItem(string title, Action handler) 136 | { 137 | if (title == null) 138 | throw new ArgumentNullException(nameof(title)); 139 | 140 | if (handler == null) 141 | throw new ArgumentNullException(nameof(handler)); 142 | 143 | _menus.EnsureNotDisposed(); 144 | 145 | var itemId = _menus.NextItemId(); 146 | 147 | if (!Plugins._plugin_menuaddentry(Handle, itemId, title)) 148 | throw new MenuException($"Failed to add menu item '{title}'."); 149 | 150 | var item = new MenuItem(this, itemId, handler); 151 | _items.Add(item); 152 | _menus._menuItemsById.Add(itemId, item); 153 | return item; 154 | } 155 | 156 | public Menu AddItem(string title, Action handler) 157 | { 158 | AddAndConfigureItem(title, handler); 159 | return this; 160 | } 161 | 162 | public Menu AddSeparator() 163 | { 164 | _menus.EnsureNotDisposed(); 165 | 166 | if (!Plugins._plugin_menuaddseparator(Handle)) 167 | throw new MenuException($"Failed to add separator."); 168 | 169 | return this; 170 | } 171 | 172 | private unsafe Menu SetIcon(byte[] iconArray) 173 | { 174 | fixed (byte* iconBytes = iconArray) 175 | { 176 | var iconStruct = new BridgeBase.ICONDATA 177 | { 178 | data = (IntPtr)iconBytes, 179 | size = (nuint)iconArray.Length 180 | }; 181 | 182 | Plugins._plugin_menuseticon(Handle, ref iconStruct); 183 | } 184 | 185 | return this; 186 | } 187 | 188 | public Menu SetIcon(Icon icon) => SetIcon(BridgeBase.ICONDATA.GetIconData(icon)); 189 | public Menu SetIcon(Image image) => SetIcon(BridgeBase.ICONDATA.GetIconData(image)); 190 | 191 | public Menu SetVisible(bool value) 192 | { 193 | Plugins._plugin_menusetvisible(_menus.PluginHandle, Handle, value); 194 | 195 | return this; 196 | } 197 | 198 | public Menu SetName(string value) 199 | { 200 | Plugins._plugin_menusetname(_menus.PluginHandle, Handle, value); 201 | 202 | return this; 203 | } 204 | 205 | public override bool Remove() 206 | { 207 | _menus.EnsureNotDisposed(); 208 | 209 | if (IsRoot || !Plugins._plugin_menuremove(Handle)) 210 | return false; 211 | 212 | Parent._items.Remove(this); 213 | return true; 214 | } 215 | 216 | public void Clear() 217 | { 218 | for (int i = _items.Count - 1; i >= 0; i--) 219 | _items[i].Remove(); 220 | } 221 | } 222 | 223 | public sealed class MenuItem : MenuItemBase 224 | { 225 | internal MenuItem(Menu parent, int id, Action handler) : base(parent) 226 | { 227 | Id = id; 228 | Handler = handler; 229 | } 230 | 231 | public int Id { get; } 232 | 233 | internal Action Handler { get; } 234 | 235 | private unsafe MenuItem SetIcon(byte[] iconArray) 236 | { 237 | fixed (byte* iconBytes = iconArray) 238 | { 239 | var iconStruct = new BridgeBase.ICONDATA 240 | { 241 | data = (IntPtr)iconBytes, 242 | size = (nuint)iconArray.Length 243 | }; 244 | 245 | Plugins._plugin_menuentryseticon(Parent._menus.PluginHandle, Id, ref iconStruct); 246 | } 247 | 248 | return this; 249 | } 250 | 251 | public MenuItem SetIcon(Icon icon) => SetIcon(BridgeBase.ICONDATA.GetIconData(icon)); 252 | public MenuItem SetIcon(Image image) => SetIcon(BridgeBase.ICONDATA.GetIconData(image)); 253 | 254 | public MenuItem SetChecked(bool value) 255 | { 256 | Plugins._plugin_menuentrysetchecked(Parent._menus.PluginHandle, Id, value); 257 | 258 | return this; 259 | } 260 | 261 | public MenuItem SetVisible(bool value) 262 | { 263 | Plugins._plugin_menuentrysetvisible(Parent._menus.PluginHandle, Id, value); 264 | 265 | return this; 266 | } 267 | 268 | public MenuItem SetName(string value) 269 | { 270 | Plugins._plugin_menuentrysetname(Parent._menus.PluginHandle, Id, value); 271 | 272 | return this; 273 | } 274 | 275 | public MenuItem SetHotKey(string value) 276 | { 277 | Plugins._plugin_menuentrysethotkey(Parent._menus.PluginHandle, Id, value); 278 | 279 | return this; 280 | } 281 | 282 | public override bool Remove() 283 | { 284 | Parent._menus.EnsureNotDisposed(); 285 | 286 | if (!Plugins._plugin_menuentryremove(Parent._menus.PluginHandle, Id)) 287 | return false; 288 | 289 | Parent._menus._menuItemsById.Remove(Id); 290 | Parent._items.Remove(this); 291 | return true; 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/EventCallbacks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Runtime.CompilerServices; 7 | using System.Threading; 8 | using DotNetPlugin.NativeBindings; 9 | using DotNetPlugin.NativeBindings.SDK; 10 | 11 | namespace DotNetPlugin 12 | { 13 | /// 14 | /// Attribute for automatically registering event callbacks in x64Dbg. 15 | /// 16 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] 17 | public class EventCallbackAttribute : Attribute 18 | { 19 | public Plugins.CBTYPE EventType { get; } 20 | 21 | public EventCallbackAttribute(Plugins.CBTYPE eventType) 22 | { 23 | EventType = eventType; 24 | } 25 | } 26 | 27 | internal static class EventCallbacks 28 | { 29 | private delegate void Callback(ref T info) where T : unmanaged; 30 | 31 | private delegate void InvokeCallbackDelegate(Callback callback, IntPtr callbackInfo) where T : unmanaged; 32 | 33 | private static readonly MethodInfo s_invokeCallbackMethodDefinition = 34 | new InvokeCallbackDelegate(InvokeCallback).Method.GetGenericMethodDefinition(); 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | private static void InvokeCallback(Callback callback, IntPtr callbackInfo) where T : unmanaged => 38 | callback(ref callbackInfo.ToStructUnsafe()); 39 | 40 | private static Plugins.CBPLUGIN BuildCallback(PluginBase plugin, MethodInfo method, Type eventInfoType) 41 | { 42 | object firstArg = method.IsStatic ? null : plugin; 43 | 44 | if (eventInfoType.IsByRef) 45 | { 46 | // ref return is not possible with expression trees (https://github.com/dotnet/csharplang/discussions/158), 47 | // so method can't be called directly, only via an indirection (InvokeCallback) 48 | 49 | eventInfoType = eventInfoType.GetElementType(); 50 | 51 | var eventTypeParam = Expression.Parameter(typeof(Plugins.CBTYPE)); 52 | var eventInfoParam = Expression.Parameter(typeof(IntPtr)); 53 | 54 | var callbackType = typeof(Callback<>).MakeGenericType(eventInfoType); 55 | var callback = Delegate.CreateDelegate(callbackType, firstArg, method, throwOnBindFailure: true); 56 | 57 | var callArgs = new Expression[] 58 | { 59 | Expression.Constant(callback, callbackType), 60 | eventInfoParam 61 | }; 62 | 63 | method = s_invokeCallbackMethodDefinition.MakeGenericMethod(eventInfoType); 64 | var call = method.IsStatic ? Expression.Call(method, callArgs) : Expression.Call(Expression.Constant(plugin), method, callArgs); 65 | 66 | var lambda = Expression.Lambda(call, eventTypeParam, eventInfoParam); 67 | 68 | return lambda.Compile(); 69 | } 70 | else 71 | { 72 | var callback = (Action)Delegate.CreateDelegate(typeof(Action), firstArg, method, throwOnBindFailure: true); 73 | return (_, info) => callback(info); 74 | } 75 | } 76 | 77 | private static bool IsValidCallbackInfoType(Plugins.CBTYPE eventType, Type eventInfoType) => eventType switch 78 | { 79 | Plugins.CBTYPE.CB_INITDEBUG => eventInfoType == typeof(Plugins.PLUG_CB_INITDEBUG), 80 | Plugins.CBTYPE.CB_STOPDEBUG => eventInfoType == typeof(Plugins.PLUG_CB_STOPDEBUG), 81 | Plugins.CBTYPE.CB_CREATEPROCESS => eventInfoType == typeof(Plugins.PLUG_CB_CREATEPROCESS), 82 | Plugins.CBTYPE.CB_EXITPROCESS => eventInfoType == typeof(Plugins.PLUG_CB_EXITPROCESS), 83 | Plugins.CBTYPE.CB_CREATETHREAD => eventInfoType == typeof(Plugins.PLUG_CB_CREATETHREAD), 84 | Plugins.CBTYPE.CB_EXITTHREAD => eventInfoType == typeof(Plugins.PLUG_CB_EXITTHREAD), 85 | Plugins.CBTYPE.CB_SYSTEMBREAKPOINT => eventInfoType == typeof(Plugins.PLUG_CB_SYSTEMBREAKPOINT), 86 | Plugins.CBTYPE.CB_LOADDLL => eventInfoType == typeof(Plugins.PLUG_CB_LOADDLL), 87 | Plugins.CBTYPE.CB_UNLOADDLL => eventInfoType == typeof(Plugins.PLUG_CB_UNLOADDLL), 88 | Plugins.CBTYPE.CB_OUTPUTDEBUGSTRING => eventInfoType == typeof(Plugins.PLUG_CB_OUTPUTDEBUGSTRING), 89 | Plugins.CBTYPE.CB_EXCEPTION => eventInfoType == typeof(Plugins.PLUG_CB_EXCEPTION), 90 | Plugins.CBTYPE.CB_BREAKPOINT => eventInfoType == typeof(Plugins.PLUG_CB_BREAKPOINT), 91 | Plugins.CBTYPE.CB_PAUSEDEBUG => eventInfoType == typeof(Plugins.PLUG_CB_PAUSEDEBUG), 92 | Plugins.CBTYPE.CB_RESUMEDEBUG => eventInfoType == typeof(Plugins.PLUG_CB_RESUMEDEBUG), 93 | Plugins.CBTYPE.CB_STEPPED => eventInfoType == typeof(Plugins.PLUG_CB_STEPPED), 94 | Plugins.CBTYPE.CB_ATTACH => eventInfoType == typeof(Plugins.PLUG_CB_ATTACH), 95 | Plugins.CBTYPE.CB_DETACH => eventInfoType == typeof(Plugins.PLUG_CB_DETACH), 96 | Plugins.CBTYPE.CB_DEBUGEVENT => eventInfoType == typeof(Plugins.PLUG_CB_DEBUGEVENT), 97 | Plugins.CBTYPE.CB_MENUENTRY => eventInfoType == typeof(Plugins.PLUG_CB_MENUENTRY), 98 | Plugins.CBTYPE.CB_WINEVENT => eventInfoType == typeof(Plugins.PLUG_CB_WINEVENT), 99 | Plugins.CBTYPE.CB_WINEVENTGLOBAL => eventInfoType == typeof(Plugins.PLUG_CB_WINEVENTGLOBAL), 100 | Plugins.CBTYPE.CB_LOADDB => eventInfoType == typeof(Plugins.PLUG_CB_LOADSAVEDB), 101 | Plugins.CBTYPE.CB_SAVEDB => eventInfoType == typeof(Plugins.PLUG_CB_LOADSAVEDB), 102 | Plugins.CBTYPE.CB_FILTERSYMBOL => eventInfoType == typeof(Plugins.PLUG_CB_FILTERSYMBOL), 103 | Plugins.CBTYPE.CB_TRACEEXECUTE => eventInfoType == typeof(Plugins.PLUG_CB_TRACEEXECUTE), 104 | Plugins.CBTYPE.CB_SELCHANGED => eventInfoType == typeof(Plugins.PLUG_CB_SELCHANGED), 105 | Plugins.CBTYPE.CB_ANALYZE => eventInfoType == typeof(Plugins.PLUG_CB_ANALYZE), 106 | Plugins.CBTYPE.CB_ADDRINFO => eventInfoType == typeof(Plugins.PLUG_CB_ADDRINFO), 107 | Plugins.CBTYPE.CB_VALFROMSTRING => eventInfoType == typeof(Plugins.PLUG_CB_VALFROMSTRING), 108 | Plugins.CBTYPE.CB_VALTOSTRING => eventInfoType == typeof(Plugins.PLUG_CB_VALTOSTRING), 109 | Plugins.CBTYPE.CB_MENUPREPARE => eventInfoType == typeof(Plugins.PLUG_CB_MENUPREPARE), 110 | Plugins.CBTYPE.CB_STOPPINGDEBUG => eventInfoType == typeof(Plugins.PLUG_CB_STOPDEBUG), 111 | _ => false 112 | }; 113 | 114 | public static IDisposable Initialize(PluginBase plugin, MethodInfo[] pluginMethods) 115 | { 116 | var registeredEventTypes = new HashSet(); 117 | 118 | var methods = pluginMethods 119 | .SelectMany(method => method.GetCustomAttributes().Select(attribute => (method, attribute))); 120 | 121 | foreach (var (method, attribute) in methods) 122 | { 123 | var eventType = attribute.EventType; 124 | 125 | if (method.ReturnType != typeof(void)) 126 | { 127 | PluginBase.LogError($"Registration of event callback {eventType} is skipped. Method '{method.Name}' has an invalid return type."); 128 | continue; 129 | } 130 | 131 | var methodParams = method.GetParameters(); 132 | ParameterInfo eventInfoParam; 133 | Type eventInfoType; 134 | if (methodParams.Length != 1 || 135 | (eventInfoType = (eventInfoParam = methodParams[0]).ParameterType) != typeof(IntPtr) && 136 | !(eventInfoType.IsByRef && !eventInfoParam.IsIn && !eventInfoParam.IsOut && IsValidCallbackInfoType(eventType, eventInfoType.GetElementType()))) 137 | { 138 | PluginBase.LogError($"Registration of event callback {eventType} is skipped. Method '{method.Name}' has an invalid signature."); 139 | continue; 140 | } 141 | 142 | if (registeredEventTypes.Contains(eventType)) 143 | { 144 | PluginBase.LogError($"Registration of event callback {eventType} failed."); 145 | continue; 146 | } 147 | 148 | Plugins._plugin_registercallback(plugin.PluginHandle, eventType, BuildCallback(plugin, method, eventInfoType)); 149 | 150 | registeredEventTypes.Add(eventType); 151 | 152 | PluginBase.LogInfo($"Event callback {eventType} registered!"); 153 | } 154 | 155 | return new Registrations(plugin, registeredEventTypes); 156 | } 157 | 158 | private sealed class Registrations : IDisposable 159 | { 160 | private PluginBase _plugin; 161 | private HashSet _registeredEventTypes; 162 | 163 | public Registrations(PluginBase plugin, HashSet registeredEventTypes) 164 | { 165 | _plugin = plugin; 166 | _registeredEventTypes = registeredEventTypes; 167 | } 168 | 169 | public void Dispose() 170 | { 171 | var plugin = Interlocked.Exchange(ref _plugin, null); 172 | 173 | if (plugin != null) 174 | { 175 | foreach (var eventType in _registeredEventTypes) 176 | { 177 | if (Plugins._plugin_unregistercallback(plugin.PluginHandle, eventType)) 178 | PluginBase.LogInfo($"Event callback {eventType} unregistered!"); 179 | else 180 | PluginBase.LogError($"Unregistration of event callback {eventType} failed."); 181 | } 182 | 183 | _registeredEventTypes = null; 184 | } 185 | } 186 | } 187 | } 188 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # X64Dbg MCP Server (plugin) 2 | This project is a starting point for building an MCP (Memory Command Protocol) server plugin for x96/x64/x32dbg https://github.com/x64dbg/x64dbg/ using C# on the classic Windows-only .NET Framework platform (No ASP.NET Core hosting required). 3 | 4 | The plugin acts as a lightweight HTTP interface bridge between an MCP client and the debugger, allowing you to have an LLM MCP client interactively send commands to inspect memory, disassemble, query registers, manipulate labels/comments, and more—all remotely and programmatically. 5 | 6 | On top of essential bindings to the x64dbg debugger engine, this template offers a clean project structure, a built-in command system, and a simple HTTP listener that exposes your commands through a text-based API. 7 | ![image](https://github.com/user-attachments/assets/4b3c3a02-edc0-48e2-93eb-a8c1727b5017) 8 | 9 | ## Features 10 | * ✅ Self-hosted HTTP command interface (no ASP.NET Core required) 11 | * ✅ Lightweight, zero-dependency binary deployment 12 | * ✅ Modular commands with parameter mapping 13 | * ✅ Direct interaction with registers, memory, threads, disassembly 14 | * ✅ Bi-directional AI/LLM command support 15 | * ✅ Plugin reload without restarting x64dbg 16 | * ✅ Expression function and menu extension support 17 | 18 | ## Cursor Support 19 | Cursor Connection: 20 | ```json 21 | { 22 | "mcpServers": { 23 | "AgentSmithers X64Dbg MCP Server": { 24 | "url": "http://127.0.0.1:50300/sse" 25 | } 26 | } 27 | } 28 | ``` 29 | ![image](https://github.com/user-attachments/assets/22414a30-d41e-4c3d-9b4f-f168f0498736) 30 | 31 | ![image](https://github.com/user-attachments/assets/53ba58e6-c97c-4c31-b57c-832951244951) 32 | 33 | ## Claude Desktop support 34 | 35 | ### MCPProxy STIDO<->SSE Bridge required: https://github.com/AgentSmithers/MCPProxy-STDIO-to-SSE/tree/master 36 | Claude Configuration Connection: 37 | ``` 38 | { 39 | "mcpServers": { 40 | "x64Dbg": { 41 | "command": "C:\\MCPProxy-STDIO-to-SSE.exe", 42 | "args": ["http://localhost:50300"] 43 | } 44 | } 45 | } 46 | ``` 47 | ![image](https://github.com/user-attachments/assets/0b089015-2270-4b39-ae23-42ce4322ba75) 48 | 49 | 50 | ![image](https://github.com/user-attachments/assets/3ef4cb69-0640-4ea0-b313-d007cdb003a8) 51 | 52 | 53 | ## Windsurf support 54 | 55 | ### MCPProxy STIDO<->SSE Bridge required: https://github.com/AgentSmithers/MCPProxy-STDIO-to-SSE/tree/master 56 | Claude Configuration Connection: 57 | ``` 58 | { 59 | "mcpServers": { 60 | "AgentSmithers x64Dbg STDIO<->SSE": { 61 | "command": "C:\\MCPProxy-STDIO-to-SSE.exe", 62 | "args": ["http://localhost:50300"] 63 | } 64 | } 65 | } 66 | ``` 67 | ![image](https://github.com/user-attachments/assets/df900c88-2291-47af-9789-1b17ff51cfa9) 68 | 69 | Known: Context deadline exceeded (timeout) issue with directly using SSE. 70 | 71 | # X64Dbg MCP Client - Need a client to sample? 72 | [mcp-csharp-sdk-client.zip](https://github.com/user-attachments/files/19697365/mcp-csharp-sdk-client.zip) 73 | 74 | Open the project 75 | Edit line 590 in Program.cs and enter your GeminiAI key from Google Cloud API. 76 | Edit line 615 in Program.cs and enter in your MCP Server IP: Location = "http://192.168.x.x:50300/sse", 77 | Open your x96 debugger, your logs should reflect that the server automatically loaded. 78 | To interact with the server by hand instead of using the AI, uncomment line 634 and comment out line 635. 79 | Hit start debug on the client and the AI should automatically execute the Prompt located on line 434 (Program.cs) 80 | 81 | ![image](https://github.com/user-attachments/assets/ebf2ad81-0672-4ceb-be6e-a44c625cd6d0) 82 | 83 | Access the latest sample client to use as a starting point of integration with this project: https://github.com/AgentSmithers/mcp-csharp-sdk-client/ 84 | 85 | ## Sample Conversations: 86 | ### AI Tasked with loading a file, counting the internal modules and begin labeling important material functions. 87 | https://github.com/AgentSmithers/x64DbgMCPServer/blob/master/Sample1 88 | 89 | ### Singleshot Speedhack identification 90 | https://github.com/AgentSmithers/x64DbgMCPServer/blob/master/Sample2 91 | 92 | ## Prerequisites 93 | To build and run this project, you'll need: 94 | - Visual Studio Build Tools (2019 v16.7 or later) 95 | - .NET Framework 4.7.2 SDK 96 | - 3F/DllExport 97 | 98 | ## Getting Started 99 | Clone or fork the project: git clone https://github.com/AgentSmithers/x64DbgMCPServer 100 | 101 | Download [DLlExport.bat](https://github.com/3F/DllExport/releases/download/1.8/DllExport.bat) and place it in the root folder of the project (Where the solutions[.sln] file is located). Then, run the `DllExport.bat`. 102 | 103 | # For X86 / 32bit support, you must have .NET Framework 2.0 and .NET Framework 3.5 installed through "add/remove windows components" in Add or remove programed (appwiz.cpl). 104 | 105 | In the DllExport GUI, 106 | 1. Check the `Installed` checkbox. 107 | 2. Set the Namespace for DllExport to `System.Runtime.InteropServices`. 108 | 3. Choose the target platform(`x64` or `x86`). 109 | 4. Click Apply. 110 | 111 | image 112 | 113 | Open the .sln solution file and build. 114 | 115 | If you get this error, clean and rebuild the DotNetPlugin.Stub 116 | image 117 | 118 | 119 | 📌 Tip: If you see `x64DbgMCPServer.dll` in the output folder, rename it to `x64DbgMCPServer.dp64` so that x64dbg can load the plugin. 120 | 121 | copy the files (x64DbgMCPServer\bin\x64\Debug) into the x64DBG plugin (x96\release\x64\plugins\x64DbgMCPServer) folder to run. 122 | Note: If the plugin folder does not exist, create it and create a x64DbgMCPServer subfolder and copy the files within. 123 | ![image](https://github.com/user-attachments/assets/8511452e-b65c-4bc8-83ff-885c384d0bbe) 124 | 125 | image 126 | 127 | 128 | Sample Debug log when loaded 129 | 130 | ![image](https://github.com/user-attachments/assets/02eb35d8-8584-46de-83c6-b535d23976b9) 131 | 132 | Start the Debugger, goto plugins -> Click "Start MCP Server" 133 | 134 | Connect to it with your prefered MCP Client on port 50300 via SSE. 135 | 136 | ### Checking command results 137 | 138 | Some x64dbg commands don't return meaningful booleans. Use these helpers: 139 | 140 | - ExecuteDebuggerCommandWithVar: run a command and read a debugger variable afterwards. 141 | Example: 142 | - `ExecuteDebuggerCommandWithVar command="init notepad.exe" resultVar=$pid pollMs=100 pollTimeoutMs=5000` 143 | - Returns the value of `$pid` (e.g., `0x1234`) after init; non-zero means started 144 | 145 | - ExecuteDebuggerCommandWithOutput: run a command and capture the log output. 146 | Example: 147 | - `ExecuteDebuggerCommandWithOutput command="bplist"` 148 | - Returns the log text produced by the command 149 | 150 | ## Troubleshooting 151 | 152 | ### "Access is denied" when starting MCP server 153 | 154 | If you see `Failed to start MCP server: Access is denied` in the x64dbg logs (Alt+L), this is because Windows requires special permissions to listen on HTTP URLs. You have two options: 155 | 156 | **Option 1: Run as Administrator (Quick fix)** 157 | - Right-click `x64dbg.exe` and select "Run as administrator" 158 | 159 | **Option 2: Grant URL permissions (Recommended)** 160 | Run these commands in an elevated PowerShell/Command Prompt: 161 | ```cmd 162 | netsh http add urlacl url=http://+:50300/sse/ user=Everyone 163 | netsh http add urlacl url=http://+:50300/message/ user=Everyone 164 | ``` 165 | 166 | After running these commands, you can start x64dbg normally and the MCP server will work. 167 | 168 | **Ensure that you run powershells "Unblock-File *" command to remove any sort of block on the downloaded files.** 169 | 170 | ### Sample Commands using the X64Dbg MCP Client 171 | I've validated several commands already and they are working wonders. I'm especially excited to be using this system to explore how AI-assisted reverse engineering could streamline security workflows. 172 | Once the MCP server is running (via the plugin menu in x64dbg), you can issue commands like: 173 | ``` 174 | ExecuteDebuggerCommand command=init C:\InjectGetTickCount\InjectSpeed.exe 175 | ExecuteDebuggerCommand command="AddFavouriteCommand Log s, NameOfCmd" 176 | ReadDismAtAddress addressStr=0x000000014000153f, byteCount=5 177 | ReadMemAtAddress addressStr=00007FFA1AC81000, byteCount=5 178 | WriteMemToAddress addressStr=0x000000014000153f, byteString=90 90 90 90 90 90 179 | CommentOrLabelAtAddress addressStr=0x000000014000153f, value=Test, mode=Comment 180 | CommentOrLabelAtAddress addressStr=0x000000014000153f, value= 181 | GetAllRegisters 182 | GetLabel addressStr=0x000000014000153f 183 | GetAllActiveThreads 184 | GetAllModulesFromMemMap 185 | GetCallStack 186 | These commands return JSON or text-formatted output that's suitable for ingestion by AI models or integration scripts. Example: 187 | ``` 188 | ![image](https://github.com/user-attachments/assets/f954feab-4518-4368-8b0a-d6ec07212122) 189 | ![image](https://github.com/user-attachments/assets/2952e4eb-76ef-460c-9124-0e3c1167fa3d) 190 | 191 | ## Debugging 192 | DotNetPlugin.Impl contains the following within the project build post commands. Update it to reflect the corret path to x64dbg for faster debugging. 193 | Upon rebuilding X64Dbg will autoload the new plugin and you can reattach to the X64Dbg instance if needed. 194 | ``` 195 | xcopy /Y /I "$(TargetDir)*.*" "C:\Users\User\Desktop\x96\release\x64\plugins\x64DbgMCPServer" 196 | C:\Users\User\Desktop\x96\release\x64\x64dbg.exe 197 | ``` 198 | ## Actively working on implementing several functions 199 | Not every command is fully implemented althrough I am actively working on getting this project moving to support full stack, thread and module dumps for the AI to query. 200 | 201 | ## How It Works 202 | The MCP server runs a simple HTTP listener and routes incoming commands to C# methods marked with the [Command] attribute. These methods can perform any logic (e.g., memory reads, disassembly, setting breakpoints) and return data in a structured format back to a MCP client. 203 | 204 | ## Known Issues 205 | ExecuteDebuggerCommand always returns true as it pertains to the comment successfully being execute and not the results of the actual command.(Fix was implemented,needs checking.)\ 206 | Currently the already compiled version is set to listen on all IP's on port 50300 thus requiring Administrative privileges. Future releases will look to detect this and will listen only on 127.0.0.1 so it may be used without administrative privileges.(See the `Troubleshooting` section) 207 | 208 | If upon launch x64/x32 dbg crashes, ensure the DLL's are not being blocked by windows. 209 | This causes .NET Framework to refuse loading the assemblies for security reasons. 210 | 211 | ## Special thanks 212 | ⚡ With the help of DotNetPluginCS by Adams85. That and roughly ~20 hours of focused coding, MCP Protocol review resulted in a decent proof-of-concept self-contained HTTP MCP server plugin for x64dbg. 213 | 214 | ## Integration Notes 215 | One of the most satisfying aspects of this project was overcoming the challenge of building an HTTP server entirely self-contained — no Kestrel, no ASP.NET, just raw HttpListener powering your reverse engineering automation. 216 | 217 | I plan to continue improving this codebase as part of my journey into AI-assisted analysis, implementation security, and automation tooling. 218 | 219 | If you'd like help creating your own integration, extending this plugin, or discussing potential use cases — feel free to reach out (see contact info in the repo or my profile). I'm eager to collaborate and learn with others exploring this space. 220 | 221 | 💻 Let's reverse engineer smarter. Not harder. 222 | 223 | Cheers 🎉 224 | 225 | Https://ControllingTheInter.net 226 | -------------------------------------------------------------------------------- /DotNetPlugin.Stub/PluginMain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Runtime.InteropServices; 5 | using System.Threading; 6 | using DotNetPlugin.NativeBindings.SDK; 7 | using RGiesecke.DllExport; 8 | using System.Drawing; 9 | 10 | namespace DotNetPlugin 11 | { 12 | /// 13 | /// Contains entry points for plugin lifecycle and debugger event callbacks. 14 | /// 15 | internal static class PluginMain 16 | { 17 | #if ALLOW_UNLOADING 18 | private static readonly Lazy NullSession = new Lazy(() => PluginSession.Null, LazyThreadSafetyMode.PublicationOnly); 19 | private static volatile Lazy s_session = NullSession; 20 | private static IPluginSession Session => s_session.Value; 21 | 22 | private static readonly string s_controlCommand = PluginBase.PluginName.Replace(' ', '_'); 23 | 24 | internal static readonly string ImplAssemblyLocation; 25 | #else 26 | private static PluginSession Session = PluginSession.Null; 27 | #endif 28 | 29 | private static int s_pluginHandle; 30 | private static Plugins.PLUG_SETUPSTRUCT s_setupStruct; 31 | 32 | private static Assembly TryLoadAssemblyFrom(AssemblyName assemblyName, string location, bool tryLoadFromMemory = false) 33 | { 34 | var pluginBasePath = Path.GetDirectoryName(location); 35 | var dllPath = Path.Combine(pluginBasePath, assemblyName.Name + ".dll"); 36 | 37 | if (!File.Exists(dllPath)) 38 | return null; 39 | 40 | if (tryLoadFromMemory) 41 | { 42 | var assemblyBytes = File.ReadAllBytes(dllPath); 43 | // first we try to load the assembly from memory so that it doesn't get locked 44 | try { return Assembly.Load(assemblyBytes); } 45 | // mixed-mode assemblies can't be loaded from memory, so we resort to loading it from the disk 46 | catch { } 47 | } 48 | 49 | return Assembly.LoadFile(dllPath); 50 | } 51 | 52 | static PluginMain() 53 | { 54 | if (AppDomain.CurrentDomain.IsDefaultAppDomain()) 55 | { 56 | AppDomain.CurrentDomain.UnhandledException += (s, e) => LogUnhandledException(e.ExceptionObject); 57 | 58 | // by default the runtime will look for referenced assemblies in the directory of the host application, 59 | // not in the plugin's dictionary, so we need to customize assembly resolving to fix this 60 | AppDomain.CurrentDomain.AssemblyResolve += (s, e) => 61 | { 62 | var assemblyName = new AssemblyName(e.Name); 63 | 64 | if (assemblyName.Name == typeof(PluginMain).Assembly.GetName().Name) 65 | return typeof(PluginMain).Assembly; 66 | 67 | return TryLoadAssemblyFrom(assemblyName, typeof(PluginMain).Assembly.Location); 68 | }; 69 | } 70 | #if ALLOW_UNLOADING 71 | else 72 | { 73 | AppDomain.CurrentDomain.AssemblyResolve += (s, e) => 74 | { 75 | var assemblyName = new AssemblyName(e.Name); 76 | 77 | if (assemblyName.Name == typeof(PluginMain).Assembly.GetName().Name) 78 | return typeof(PluginMain).Assembly; 79 | 80 | return 81 | (ImplAssemblyLocation != null ? TryLoadAssemblyFrom(assemblyName, ImplAssemblyLocation, tryLoadFromMemory: true) : null) ?? 82 | TryLoadAssemblyFrom(assemblyName, typeof(PluginMain).Assembly.Location, tryLoadFromMemory: true); 83 | }; 84 | } 85 | 86 | using (var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("build.meta")) 87 | { 88 | if (resourceStream == null) 89 | return; 90 | 91 | ImplAssemblyLocation = new StreamReader(resourceStream).ReadLine(); 92 | } 93 | #endif 94 | } 95 | 96 | public static void LogUnhandledException(object exceptionObject) 97 | { 98 | var location = typeof(PluginMain).Assembly.Location; 99 | var logPath = Path.ChangeExtension(location, ".log"); 100 | 101 | var errorMessage = exceptionObject?.ToString(); 102 | if (errorMessage != null) 103 | { 104 | errorMessage += Environment.NewLine; 105 | File.AppendAllText(logPath, errorMessage); 106 | PluginBase.LogError(errorMessage); 107 | } 108 | } 109 | 110 | #if ALLOW_UNLOADING 111 | private static void HandleImplChanged(object sender) 112 | { 113 | var session = s_session; 114 | if (ReferenceEquals(session.Value, sender) && UnloadPlugin(session)) 115 | LoadPlugin(session); 116 | } 117 | 118 | private static bool LoadPlugin(Lazy reloadedSession = null) 119 | { 120 | if (!TryLoadPlugin(isInitial: false, reloadedSession)) 121 | { 122 | PluginBase.LogError("Failed to load the implementation assembly."); 123 | return false; 124 | } 125 | 126 | Session.PluginHandle = s_pluginHandle; 127 | 128 | if (!Session.Init()) 129 | { 130 | PluginBase.LogError("Failed to initialize the implementation assembly."); 131 | TryUnloadPlugin(); 132 | return false; 133 | } 134 | 135 | Session.Setup(ref s_setupStruct); 136 | 137 | PluginBase.LogInfo("Successfully loaded the implementation assembly."); 138 | return true; 139 | } 140 | 141 | private static bool UnloadPlugin(Lazy reloadedSession = null) 142 | { 143 | if (!TryUnloadPlugin(reloadedSession)) 144 | { 145 | PluginBase.LogError("Failed to unload the implementation assembly."); 146 | return false; 147 | } 148 | 149 | PluginBase.LogInfo("Successfully unloaded the implementation assembly."); 150 | return true; 151 | } 152 | 153 | private static bool TryLoadPlugin(bool isInitial, Lazy reloadedSession = null) 154 | { 155 | var expectedSession = reloadedSession ?? NullSession; 156 | var newSession = new Lazy(() => new PluginSessionProxy(HandleImplChanged), LazyThreadSafetyMode.ExecutionAndPublication); 157 | var originalSession = Interlocked.CompareExchange(ref s_session, newSession, expectedSession); 158 | if (originalSession == expectedSession) 159 | { 160 | _ = newSession.Value; // forces creation of session 161 | 162 | return true; 163 | } 164 | 165 | return false; 166 | } 167 | 168 | private static bool TryUnloadPlugin(Lazy reloadedSession = null) 169 | { 170 | Lazy originalSession; 171 | 172 | if (reloadedSession == null) 173 | { 174 | originalSession = Interlocked.Exchange(ref s_session, NullSession); 175 | } 176 | else 177 | { 178 | originalSession = 179 | Interlocked.CompareExchange(ref s_session, reloadedSession, reloadedSession) == reloadedSession ? 180 | reloadedSession : 181 | NullSession; 182 | } 183 | 184 | if (originalSession != NullSession) 185 | { 186 | originalSession.Value.Dispose(); 187 | return true; 188 | } 189 | 190 | return false; 191 | } 192 | 193 | #else 194 | private static bool TryLoadPlugin(bool isInitial) 195 | { 196 | if (isInitial) 197 | { 198 | Session = new PluginSession(); 199 | return true; 200 | } 201 | 202 | return false; 203 | } 204 | 205 | private static bool TryUnloadPlugin() 206 | { 207 | return false; 208 | } 209 | #endif 210 | 211 | [RGiesecke.DllExport.DllExport("pluginit", CallingConvention.Cdecl)] 212 | public static bool pluginit(ref Plugins.PLUG_INITSTRUCT initStruct) 213 | { 214 | if (!TryLoadPlugin(isInitial: true)) 215 | return false; 216 | 217 | initStruct.sdkVersion = Plugins.PLUG_SDKVERSION; 218 | initStruct.pluginVersion = PluginBase.PluginVersion; 219 | initStruct.pluginName = PluginBase.PluginName; 220 | Session.PluginHandle = s_pluginHandle = initStruct.pluginHandle; 221 | 222 | #if ALLOW_UNLOADING 223 | if (!Plugins._plugin_registercommand(s_pluginHandle, s_controlCommand, ControlCommand, false)) 224 | { 225 | PluginBase.LogError($"Failed to register the \"'{s_controlCommand}'\" command."); 226 | TryUnloadPlugin(); 227 | return false; 228 | } 229 | #endif 230 | 231 | if (!Session.Init()) 232 | { 233 | PluginBase.LogError("Failed to initialize the implementation assembly."); 234 | TryUnloadPlugin(); 235 | return false; 236 | } 237 | 238 | return true; 239 | } 240 | 241 | [RGiesecke.DllExport.DllExport("plugsetup", CallingConvention.Cdecl)] 242 | private static void plugsetup(ref Plugins.PLUG_SETUPSTRUCT setupStruct) 243 | { 244 | s_setupStruct = setupStruct; 245 | 246 | Session.Setup(ref setupStruct); 247 | } 248 | 249 | [RGiesecke.DllExport.DllExport("plugstop", CallingConvention.Cdecl)] 250 | private static bool plugstop() 251 | { 252 | var success = Session.Stop(); 253 | 254 | #if ALLOW_UNLOADING 255 | Plugins._plugin_unregistercommand(s_pluginHandle, s_controlCommand); 256 | #endif 257 | 258 | s_setupStruct = default; 259 | s_pluginHandle = default; 260 | 261 | return success; 262 | } 263 | 264 | #if ALLOW_UNLOADING 265 | private static bool ControlCommand(string[] args) 266 | { 267 | if (args.Length > 1) 268 | { 269 | if ("load".Equals(args[1], StringComparison.OrdinalIgnoreCase)) 270 | { 271 | return LoadPlugin(); 272 | } 273 | else if ("unload".Equals(args[1], StringComparison.OrdinalIgnoreCase)) 274 | { 275 | return UnloadPlugin(); 276 | } 277 | } 278 | 279 | PluginBase.LogError($"Invalid syntax. Usage: {s_controlCommand} [load|unload]"); 280 | return false; 281 | } 282 | #endif 283 | 284 | [RGiesecke.DllExport.DllExport("CBMENUENTRY", CallingConvention.Cdecl)] 285 | public static void CBMENUENTRY(Plugins.CBTYPE cbType, ref Plugins.PLUG_CB_MENUENTRY info) 286 | { 287 | Session.OnMenuEntry(ref info); 288 | } 289 | 290 | [RGiesecke.DllExport.DllExport("plugingeticon", CallingConvention.Cdecl)] 291 | public static IntPtr plugingeticon() 292 | { 293 | // Icon handled via managed menu icon in Impl; no stub icon. 294 | return IntPtr.Zero; 295 | } 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/SDK/TitanEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DotNetPlugin.NativeBindings.SDK 5 | { 6 | // https://github.com/x64dbg/TitanEngine/blob/x64dbg/SDK/C/TitanEngine.h 7 | public static class TitanEngine 8 | { 9 | public const int UE_STRUCT_PE32STRUCT = 1; 10 | public const int UE_STRUCT_PE64STRUCT = 2; 11 | public const int UE_STRUCT_PESTRUCT = 3; 12 | public const int UE_STRUCT_IMPORTENUMDATA = 4; 13 | public const int UE_STRUCT_THREAD_ITEM_DATA = 5; 14 | public const int UE_STRUCT_LIBRARY_ITEM_DATA = 6; 15 | public const int UE_STRUCT_LIBRARY_ITEM_DATAW = 7; 16 | public const int UE_STRUCT_PROCESS_ITEM_DATA = 8; 17 | public const int UE_STRUCT_HANDLERARRAY = 9; 18 | public const int UE_STRUCT_PLUGININFORMATION = 10; 19 | public const int UE_STRUCT_HOOK_ENTRY = 11; 20 | public const int UE_STRUCT_FILE_STATUS_INFO = 12; 21 | public const int UE_STRUCT_FILE_FIX_INFO = 13; 22 | public const int UE_STRUCT_X87FPUREGISTER = 14; 23 | public const int UE_STRUCT_X87FPU = 15; 24 | public const int UE_STRUCT_TITAN_ENGINE_CONTEXT = 16; 25 | public const int UE_ACCESS_READ = 0; 26 | public const int UE_ACCESS_WRITE = 1; 27 | public const int UE_ACCESS_ALL = 2; 28 | public const int UE_HIDE_PEBONLY = 0; 29 | public const int UE_HIDE_BASIC = 1; 30 | public const int UE_PLUGIN_CALL_REASON_PREDEBUG = 1; 31 | public const int UE_PLUGIN_CALL_REASON_EXCEPTION = 2; 32 | public const int UE_PLUGIN_CALL_REASON_POSTDEBUG = 3; 33 | public const int UE_PLUGIN_CALL_REASON_UNHANDLEDEXCEPTION = 4; 34 | public const int TEE_HOOK_NRM_JUMP = 1; 35 | public const int TEE_HOOK_NRM_CALL = 3; 36 | public const int TEE_HOOK_IAT = 5; 37 | public const int UE_ENGINE_ALOW_MODULE_LOADING = 1; 38 | public const int UE_ENGINE_AUTOFIX_FORWARDERS = 2; 39 | public const int UE_ENGINE_PASS_ALL_EXCEPTIONS = 3; 40 | public const int UE_ENGINE_NO_CONSOLE_WINDOW = 4; 41 | public const int UE_ENGINE_BACKUP_FOR_CRITICAL_FUNCTIONS = 5; 42 | public const int UE_ENGINE_CALL_PLUGIN_CALLBACK = 6; 43 | public const int UE_ENGINE_RESET_CUSTOM_HANDLER = 7; 44 | public const int UE_ENGINE_CALL_PLUGIN_DEBUG_CALLBACK = 8; 45 | public const int UE_ENGINE_SET_DEBUG_PRIVILEGE = 9; 46 | public const int UE_OPTION_REMOVEALL = 1; 47 | public const int UE_OPTION_DISABLEALL = 2; 48 | public const int UE_OPTION_REMOVEALLDISABLED = 3; 49 | public const int UE_OPTION_REMOVEALLENABLED = 4; 50 | public const int UE_STATIC_DECRYPTOR_XOR = 1; 51 | public const int UE_STATIC_DECRYPTOR_SUB = 2; 52 | public const int UE_STATIC_DECRYPTOR_ADD = 3; 53 | public const int UE_STATIC_DECRYPTOR_FOREWARD = 1; 54 | public const int UE_STATIC_DECRYPTOR_BACKWARD = 2; 55 | public const int UE_STATIC_KEY_SIZE_1 = 1; 56 | public const int UE_STATIC_KEY_SIZE_2 = 2; 57 | public const int UE_STATIC_KEY_SIZE_4 = 4; 58 | public const int UE_STATIC_KEY_SIZE_8 = 8; 59 | public const int UE_STATIC_APLIB = 1; 60 | public const int UE_STATIC_APLIB_DEPACK = 2; 61 | public const int UE_STATIC_LZMA = 3; 62 | public const int UE_STATIC_HASH_MD5 = 1; 63 | public const int UE_STATIC_HASH_SHA1 = 2; 64 | public const int UE_STATIC_HASH_CRC32 = 3; 65 | public const int UE_RESOURCE_LANGUAGE_ANY = -1; 66 | public const int UE_PE_OFFSET = 0; 67 | public const int UE_IMAGEBASE = 1; 68 | public const int UE_OEP = 2; 69 | public const int UE_SIZEOFIMAGE = 3; 70 | public const int UE_SIZEOFHEADERS = 4; 71 | public const int UE_SIZEOFOPTIONALHEADER = 5; 72 | public const int UE_SECTIONALIGNMENT = 6; 73 | public const int UE_IMPORTTABLEADDRESS = 7; 74 | public const int UE_IMPORTTABLESIZE = 8; 75 | public const int UE_RESOURCETABLEADDRESS = 9; 76 | public const int UE_RESOURCETABLESIZE = 10; 77 | public const int UE_EXPORTTABLEADDRESS = 11; 78 | public const int UE_EXPORTTABLESIZE = 12; 79 | public const int UE_TLSTABLEADDRESS = 13; 80 | public const int UE_TLSTABLESIZE = 14; 81 | public const int UE_RELOCATIONTABLEADDRESS = 15; 82 | public const int UE_RELOCATIONTABLESIZE = 16; 83 | public const int UE_TIMEDATESTAMP = 17; 84 | public const int UE_SECTIONNUMBER = 18; 85 | public const int UE_CHECKSUM = 19; 86 | public const int UE_SUBSYSTEM = 20; 87 | public const int UE_CHARACTERISTICS = 21; 88 | public const int UE_NUMBEROFRVAANDSIZES = 22; 89 | public const int UE_BASEOFCODE = 23; 90 | public const int UE_BASEOFDATA = 24; 91 | public const int UE_SECTIONNAME = 40; 92 | public const int UE_SECTIONVIRTUALOFFSET = 41; 93 | public const int UE_SECTIONVIRTUALSIZE = 42; 94 | public const int UE_SECTIONRAWOFFSET = 43; 95 | public const int UE_SECTIONRAWSIZE = 44; 96 | public const int UE_SECTIONFLAGS = 45; 97 | public const int UE_VANOTFOUND = -2; 98 | public const int UE_CH_BREAKPOINT = 1; 99 | public const int UE_CH_SINGLESTEP = 2; 100 | public const int UE_CH_ACCESSVIOLATION = 3; 101 | public const int UE_CH_ILLEGALINSTRUCTION = 4; 102 | public const int UE_CH_NONCONTINUABLEEXCEPTION = 5; 103 | public const int UE_CH_ARRAYBOUNDSEXCEPTION = 6; 104 | public const int UE_CH_FLOATDENORMALOPERAND = 7; 105 | public const int UE_CH_FLOATDEVIDEBYZERO = 8; 106 | public const int UE_CH_INTEGERDEVIDEBYZERO = 9; 107 | public const int UE_CH_INTEGEROVERFLOW = 10; 108 | public const int UE_CH_PRIVILEGEDINSTRUCTION = 11; 109 | public const int UE_CH_PAGEGUARD = 12; 110 | public const int UE_CH_EVERYTHINGELSE = 13; 111 | public const int UE_CH_CREATETHREAD = 14; 112 | public const int UE_CH_EXITTHREAD = 15; 113 | public const int UE_CH_CREATEPROCESS = 16; 114 | public const int UE_CH_EXITPROCESS = 17; 115 | public const int UE_CH_LOADDLL = 18; 116 | public const int UE_CH_UNLOADDLL = 19; 117 | public const int UE_CH_OUTPUTDEBUGSTRING = 20; 118 | public const int UE_CH_AFTEREXCEPTIONPROCESSING = 21; 119 | public const int UE_CH_SYSTEMBREAKPOINT = 23; 120 | public const int UE_CH_UNHANDLEDEXCEPTION = 24; 121 | public const int UE_CH_RIPEVENT = 25; 122 | public const int UE_CH_DEBUGEVENT = 26; 123 | public const int UE_OPTION_HANDLER_RETURN_HANDLECOUNT = 1; 124 | public const int UE_OPTION_HANDLER_RETURN_ACCESS = 2; 125 | public const int UE_OPTION_HANDLER_RETURN_FLAGS = 3; 126 | public const int UE_OPTION_HANDLER_RETURN_TYPENAME = 4; 127 | public const int UE_BREAKPOINT_INT3 = 1; 128 | public const int UE_BREAKPOINT_LONG_INT3 = 2; 129 | public const int UE_BREAKPOINT_UD2 = 3; 130 | public const int UE_BPXREMOVED = 0; 131 | public const int UE_BPXACTIVE = 1; 132 | public const int UE_BPXINACTIVE = 2; 133 | public const int UE_BREAKPOINT = 0; 134 | public const int UE_SINGLESHOOT = 1; 135 | public const int UE_HARDWARE = 2; 136 | public const int UE_MEMORY = 3; 137 | public const int UE_MEMORY_READ = 4; 138 | public const int UE_MEMORY_WRITE = 5; 139 | public const int UE_MEMORY_EXECUTE = 6; 140 | public const int UE_BREAKPOINT_TYPE_INT3 = 268435456; 141 | public const int UE_BREAKPOINT_TYPE_LONG_INT3 = 536870912; 142 | public const int UE_BREAKPOINT_TYPE_UD2 = 805306368; 143 | public const int UE_HARDWARE_EXECUTE = 4; 144 | public const int UE_HARDWARE_WRITE = 5; 145 | public const int UE_HARDWARE_READWRITE = 6; 146 | public const int UE_HARDWARE_SIZE_1 = 7; 147 | public const int UE_HARDWARE_SIZE_2 = 8; 148 | public const int UE_HARDWARE_SIZE_4 = 9; 149 | public const int UE_HARDWARE_SIZE_8 = 10; 150 | public const int UE_ON_LIB_LOAD = 1; 151 | public const int UE_ON_LIB_UNLOAD = 2; 152 | public const int UE_ON_LIB_ALL = 3; 153 | public const int UE_APISTART = 0; 154 | public const int UE_APIEND = 1; 155 | public const int UE_PLATFORM_x86 = 1; 156 | public const int UE_PLATFORM_x64 = 2; 157 | public const int UE_PLATFORM_ALL = 3; 158 | public const int UE_FUNCTION_STDCALL = 1; 159 | public const int UE_FUNCTION_CCALL = 2; 160 | public const int UE_FUNCTION_FASTCALL = 3; 161 | public const int UE_FUNCTION_STDCALL_RET = 4; 162 | public const int UE_FUNCTION_CCALL_RET = 5; 163 | public const int UE_FUNCTION_FASTCALL_RET = 6; 164 | public const int UE_FUNCTION_STDCALL_CALL = 7; 165 | public const int UE_FUNCTION_CCALL_CALL = 8; 166 | public const int UE_FUNCTION_FASTCALL_CALL = 9; 167 | public const int UE_PARAMETER_BYTE = 0; 168 | public const int UE_PARAMETER_WORD = 1; 169 | public const int UE_PARAMETER_DWORD = 2; 170 | public const int UE_PARAMETER_QWORD = 3; 171 | public const int UE_PARAMETER_PTR_BYTE = 4; 172 | public const int UE_PARAMETER_PTR_WORD = 5; 173 | public const int UE_PARAMETER_PTR_DWORD = 6; 174 | public const int UE_PARAMETER_PTR_QWORD = 7; 175 | public const int UE_PARAMETER_STRING = 8; 176 | public const int UE_PARAMETER_UNICODE = 9; 177 | public const int UE_EAX = 1; 178 | public const int UE_EBX = 2; 179 | public const int UE_ECX = 3; 180 | public const int UE_EDX = 4; 181 | public const int UE_EDI = 5; 182 | public const int UE_ESI = 6; 183 | public const int UE_EBP = 7; 184 | public const int UE_ESP = 8; 185 | public const int UE_EIP = 9; 186 | public const int UE_EFLAGS = 10; 187 | public const int UE_DR0 = 11; 188 | public const int UE_DR1 = 12; 189 | public const int UE_DR2 = 13; 190 | public const int UE_DR3 = 14; 191 | public const int UE_DR6 = 15; 192 | public const int UE_DR7 = 16; 193 | public const int UE_RAX = 17; 194 | public const int UE_RBX = 18; 195 | public const int UE_RCX = 19; 196 | public const int UE_RDX = 20; 197 | public const int UE_RDI = 21; 198 | public const int UE_RSI = 22; 199 | public const int UE_RBP = 23; 200 | public const int UE_RSP = 24; 201 | public const int UE_RIP = 25; 202 | public const int UE_RFLAGS = 26; 203 | public const int UE_R8 = 27; 204 | public const int UE_R9 = 28; 205 | public const int UE_R10 = 29; 206 | public const int UE_R11 = 30; 207 | public const int UE_R12 = 31; 208 | public const int UE_R13 = 32; 209 | public const int UE_R14 = 33; 210 | public const int UE_R15 = 34; 211 | public const int UE_CIP = 35; 212 | public const int UE_CSP = 36; 213 | 214 | [DllImport("TitanEngine.dll", CallingConvention = CallingConvention.Cdecl)] 215 | public static extern UIntPtr GetContextData(uint IndexOfRegister); 216 | 217 | [DllImport("TitanEngine.dll", CallingConvention = CallingConvention.Cdecl)] 218 | public static extern IntPtr TitanGetProcessInformation(); 219 | 220 | [DllImport("TitanEngine.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)] 221 | public static extern bool DumpProcess(IntPtr hProcess, IntPtr ImageBase, string szDumpFileName, UIntPtr EntryPoint); 222 | 223 | [DllImport("TitanEngine.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)] 224 | public static extern bool StaticFileLoad(string szFileName, uint DesiredAccess, bool SimulateLoad, IntPtr FileHandle, ref uint LoadedSize, IntPtr FileMap, IntPtr FileMapVA); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace DotNetPlugin.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DotNetPlugin.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap AboutIcon { 67 | get { 68 | object obj = ResourceManager.GetObject("AboutIcon", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap MainIcon { 77 | get { 78 | object obj = ResourceManager.GetObject("MainIcon", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized string similar to Debugger Command Help Manual 85 | /// 86 | ///InitDebug 87 | ///ExecuteDebuggerCommand command = InitDebug executable_path [command_line] [current_folder] 88 | ///[string]executable_path = Path to the executable file to debug. If a full path is not provided, the current directory is used. Enclose paths with spaces in quotation marks. 89 | ///[optional string]command_line = Command line arguments to pass to the process. 90 | ///[optional string]current_folder = Working directory for the process. 91 | ///Description: Initializes the debugger by loading the [rest of string was truncated]";. 92 | /// 93 | internal static string DebugControl { 94 | get { 95 | return ResourceManager.GetString("DebugControl", resourceCulture); 96 | } 97 | } 98 | 99 | /// 100 | /// Looks up a localized string similar to [ 101 | /// { 102 | /// "command": "InitDebug", 103 | /// "aliases": ["initdbg", "init"], 104 | /// "description": "Initializes the debugger by loading the specified executable, performing basic checks, setting breakpoints on TLS callbacks (if present), and at the process entry point. It breaks at the system breakpoint before returning control to the user.", 105 | /// "arguments": [ 106 | /// { 107 | /// "name": "executable_path", 108 | /// "type": "string", 109 | /// "description": "Path to the executable file to debug. If a full path is [rest of string was truncated]";. 110 | /// 111 | internal static string DebugControlJSON { 112 | get { 113 | return ResourceManager.GetString("DebugControlJSON", resourceCulture); 114 | } 115 | } 116 | 117 | /// 118 | /// Looks up a localized string similar to disasm 119 | ///ExecuteDebuggerCommand command = disasm address 120 | ///[integer]address = The memory address to display in the disassembly view. 121 | ///Description: Opens the disassembly view at the specified address. Aliases: dis, d. 122 | ///Example: 123 | ///ExecuteDebuggerCommand command = "dis 0x00401000" 124 | ///ExecuteDebuggerCommand command = "d MyFunctionEntry" 125 | /// 126 | ///dump 127 | ///ExecuteDebuggerCommand command = dump address 128 | ///[integer]address = The memory address to display in the dump view. 129 | ///Description: Opens the dump view at the specified address. [rest of string was truncated]";. 130 | /// 131 | internal static string GUI { 132 | get { 133 | return ResourceManager.GetString("GUI", resourceCulture); 134 | } 135 | } 136 | 137 | /// 138 | /// Looks up a localized string similar to [ 139 | /// { 140 | /// "command": "disasm", 141 | /// "aliases": ["dis", "d"], 142 | /// "description": "Opens the disassembly view at the specified address.", 143 | /// "arguments": [ 144 | /// { 145 | /// "name": "address", 146 | /// "type": "integer", 147 | /// "description": "The memory address to display in the disassembly view." 148 | /// } 149 | /// ], 150 | /// "result": null 151 | /// }, 152 | /// { 153 | /// "command": "dump", 154 | /// "aliases": [], 155 | /// "description": "Opens the dump view at the specified address.", 156 | /// "arguments": [ 157 | /// { 158 | /// "name": [rest of string was truncated]";. 159 | /// 160 | internal static string GUIJSON { 161 | get { 162 | return ResourceManager.GetString("GUIJSON", resourceCulture); 163 | } 164 | } 165 | 166 | /// 167 | /// Looks up a localized string similar to find 168 | ///ExecuteDebuggerCommand command = find start_address pattern [size] 169 | ///[integer]start_address = The address to start searching from. The search stops at the end of the memory page. 170 | ///[string]pattern = The byte pattern to search for, which can include wildcards (e.g., 'EB0?90??8D'). 171 | ///[optional integer]size = The size of the data to search in. Defaults to the size of the memory region. 172 | ///Description: Find a pattern in a memory page. 173 | ///Result: $result (integer) = The virtual address where the pattern is found, [rest of string was truncated]";. 174 | /// 175 | internal static string Search { 176 | get { 177 | return ResourceManager.GetString("Search", resourceCulture); 178 | } 179 | } 180 | 181 | /// 182 | /// Looks up a localized string similar to [ 183 | /// { 184 | /// "command": "find", 185 | /// "aliases": [], 186 | /// "description": "Find a pattern in a memory page.", 187 | /// "arguments": [ 188 | /// { 189 | /// "name": "start_address", 190 | /// "type": "integer", 191 | /// "description": "The address to start searching from. The search stops at the end of the memory page." 192 | /// }, 193 | /// { 194 | /// "name": "pattern", 195 | /// "type": "string", 196 | /// "description": "The byte pattern to search for, which can include wildcards (e.g., 'EB0?90??8D')." 197 | /// }, 198 | /// { 199 | /// [rest of string was truncated]";. 200 | /// 201 | internal static string SearchJSON { 202 | get { 203 | return ResourceManager.GetString("SearchJSON", resourceCulture); 204 | } 205 | } 206 | 207 | /// 208 | /// Looks up a localized string similar to createthread 209 | ///ExecuteDebuggerCommand command = createthread entry_point [argument] 210 | ///[integer]entry_point = The memory address where the new thread will begin execution. 211 | ///[optional integer]argument = The argument to pass to the new thread. Defaults to 0 if not specified. 212 | ///Description: Creates a new thread at the specified entry point. Aliases: threadcreate, newthread, threadnew. 213 | ///Result: $result (integer) = The thread ID of the newly created thread. 214 | ///Example: 215 | ///ExecuteDebuggerCommand command = "createthread 0 [rest of string was truncated]";. 216 | /// 217 | internal static string ThreadControl { 218 | get { 219 | return ResourceManager.GetString("ThreadControl", resourceCulture); 220 | } 221 | } 222 | 223 | /// 224 | /// Looks up a localized string similar to [ 225 | /// { 226 | /// "command": "createthread", 227 | /// "aliases": ["threadcreate", "newthread", "threadnew"], 228 | /// "description": "Creates a new thread at the specified entry point.", 229 | /// "arguments": [ 230 | /// { 231 | /// "name": "entry_point", 232 | /// "type": "integer", 233 | /// "description": "The memory address where the new thread will begin execution." 234 | /// }, 235 | /// { 236 | /// "name": "argument", 237 | /// "type": "integer", 238 | /// "description": "The argument to pass to the new thread. Defaults to 0 if not [rest of string was truncated]";. 239 | /// 240 | internal static string ThreadControlJSON { 241 | get { 242 | return ResourceManager.GetString("ThreadControlJSON", resourceCulture); 243 | } 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/SDK/Bridge.Dbg.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace DotNetPlugin.NativeBindings.SDK 6 | { 7 | // https://github.com/x64dbg/x64dbg/blob/development/src/bridge/bridgemain.h 8 | partial class Bridge 9 | { 10 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 11 | public static extern bool DbgCmdExec([MarshalAs(UnmanagedType.LPUTF8Str)] string cmd); 12 | 13 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 14 | public static extern bool DbgCmdExecDirect([MarshalAs(UnmanagedType.LPUTF8Str)] string cmd); 15 | 16 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 17 | public static extern void DbgDisasmFastAt(nuint addr, ref BASIC_INSTRUCTION_INFO basicinfo); 18 | 19 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 20 | public static extern nuint DbgGetBranchDestination(nuint addr); 21 | 22 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 23 | private static extern bool DbgGetCommentAt(nuint addr, IntPtr text); 24 | 25 | public static unsafe bool DbgGetCommentAt(nuint addr, out string text) 26 | { 27 | var textBufferPtr = stackalloc byte[MAX_COMMENT_SIZE]; 28 | var success = DbgGetCommentAt(addr, new IntPtr(textBufferPtr)); 29 | text = success ? new IntPtr(textBufferPtr).MarshalToStringUTF8(MAX_COMMENT_SIZE) : default; 30 | return success; 31 | } 32 | 33 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 34 | private static extern bool DbgGetLabelAt(nuint addr, SEGMENTREG segment, IntPtr text); 35 | 36 | public static unsafe bool DbgGetLabelAt(nuint addr, SEGMENTREG segment, out string text) 37 | { 38 | var textBufferPtr = stackalloc byte[MAX_LABEL_SIZE]; 39 | var success = DbgGetLabelAt(addr, segment, new IntPtr(textBufferPtr)); 40 | text = success ? new IntPtr(textBufferPtr).MarshalToStringUTF8(MAX_LABEL_SIZE) : default; 41 | return success; 42 | } 43 | 44 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 45 | private static extern bool DbgGetModuleAt(nuint addr, IntPtr text); 46 | 47 | public static unsafe bool DbgGetModuleAt(nuint addr, out string text) 48 | { 49 | var textBufferPtr = stackalloc byte[MAX_MODULE_SIZE]; 50 | var success = DbgGetModuleAt(addr, new IntPtr(textBufferPtr)); 51 | text = success ? new IntPtr(textBufferPtr).MarshalToStringUTF8(MAX_MODULE_SIZE) : default; 52 | return success; 53 | } 54 | 55 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 56 | public static extern bool DbgIsDebugging(); 57 | 58 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 59 | public static extern bool DbgIsRunning(); 60 | 61 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 62 | public static extern nuint DbgModBaseFromName([MarshalAs(UnmanagedType.LPUTF8Str)] string name); 63 | 64 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 65 | public static extern nuint DbgValFromString([MarshalAs(UnmanagedType.LPUTF8Str)] string @string); 66 | 67 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 68 | public static extern bool DbgValToString([MarshalAs(UnmanagedType.LPUTF8Str)] string @string, nuint value); 69 | 70 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 71 | public static extern void DbgDisasmAt(nuint addr, ref DISASM_INSTR instr); 72 | 73 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 74 | public static extern nuint DbgMemFindBaseAddr(nuint addr, out nuint size); 75 | 76 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 77 | public static extern bool DbgSetCommentAt(nuint addr, [MarshalAs(UnmanagedType.LPUTF8Str)] string text); 78 | 79 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 80 | public static extern bool DbgSetLabelAt(nuint addr, [MarshalAs(UnmanagedType.LPUTF8Str)] string text); 81 | 82 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 83 | public static extern bool DbgSetAutoCommentAt(nuint addr, [MarshalAs(UnmanagedType.LPUTF8Str)] string text); 84 | 85 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 86 | public static extern bool DbgSetAutoLabelAt(nuint addr, [MarshalAs(UnmanagedType.LPUTF8Str)] string text); 87 | 88 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 89 | public static extern void DbgClearAutoCommentRange(nuint start, nuint end); 90 | 91 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 92 | public static extern void DbgClearAutoLabelRange(nuint start, nuint end); 93 | 94 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 95 | public static extern void DbgClearCommentRange(nuint start, nuint end); 96 | 97 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 98 | public static extern void DbgClearLabelRange(nuint start, nuint end); 99 | 100 | 101 | 102 | 103 | 104 | [Flags] 105 | public enum ADDRINFOFLAGS 106 | { 107 | flagmodule = 0x1, 108 | flaglabel = 0x2, 109 | flagcomment = 0x4, 110 | flagbookmark = 0x8, 111 | flagfunction = 0x10, 112 | flagloop = 0x20, 113 | flagargs = 0x40, 114 | flagNoFuncOffset = 0x80 115 | } 116 | 117 | // Represents FUNCTION, LOOP, ARG structures (simplified) 118 | [StructLayout(LayoutKind.Sequential, Pack = NativePacking)] 119 | public struct FUNCTION_LOOP_INFO // Name adjusted for clarity 120 | { 121 | public nuint start; 122 | public nuint end; 123 | public nuint instrcount; 124 | // Note: The C++ FUNCTION_LOOP_INFO might have other fields like 'manual', 'depth' 125 | // which would need to be added here if flags indicate they are used/valid. 126 | // For basic symbol lookup, these might not be essential. 127 | } 128 | 129 | // Struct to receive data from _dbg_addrinfoget, using StringBuilder for output strings 130 | // In NativeMethods.cs 131 | 132 | // Struct to pass to _dbg_addrinfoget, using IntPtr for output string buffers 133 | [StructLayout(LayoutKind.Sequential, Pack = NativePacking, CharSet = CharSet.Ansi)] 134 | public struct BRIDGE_ADDRINFO_NATIVE // Renamed for clarity 135 | { 136 | public ADDRINFOFLAGS flags; // Input: Flags indicating what info to retrieve 137 | public IntPtr module; // Output: Pointer to buffer for Module name (SizeConst=256) 138 | public IntPtr label; // Output: Pointer to buffer for Label name (SizeConst=256) 139 | public IntPtr comment; // Output: Pointer to buffer for Comment text (SizeConst=512) 140 | [MarshalAs(UnmanagedType.Bool)] 141 | public bool isbookmark; // Output: Bookmark status 142 | public FUNCTION_LOOP_INFO function; // Output: Function info 143 | public FUNCTION_LOOP_INFO loop; // Output: Loop info 144 | public FUNCTION_LOOP_INFO args; // Output: Argument info 145 | } 146 | 147 | // Update the P/Invoke signature to use the new struct name 148 | [DllImport("x64dbg.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "_dbg_addrinfoget", ExactSpelling = true, CharSet = CharSet.Ansi)] 149 | [return: MarshalAs(UnmanagedType.Bool)] 150 | public static extern bool DbgAddrInfoGet(nuint addr, int segment, ref BRIDGE_ADDRINFO_NATIVE addrinfo); // Use new struct 151 | 152 | 153 | 154 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 155 | private static extern bool DbgMemRead(nuint va, IntPtr dest, nuint size); 156 | 157 | public static unsafe bool DbgMemRead(nuint va, T[] buffer, nuint size) where T : unmanaged 158 | { 159 | if (buffer is null || size > (nuint)buffer.Length) return false; 160 | 161 | fixed (T* ptr = buffer) 162 | { 163 | return DbgMemRead(va, (IntPtr)ptr, size); 164 | } 165 | } 166 | 167 | public static unsafe bool DbgMemRead(nuint va, ref T dest, nuint size) where T : struct 168 | { 169 | if (size > (nuint)Marshal.SizeOf(dest)) return false; 170 | 171 | var handle = GCHandle.Alloc(dest, GCHandleType.Pinned); 172 | try 173 | { 174 | var success = DbgMemRead(va, handle.AddrOfPinnedObject(), size); 175 | dest = success ? Marshal.PtrToStructure(handle.AddrOfPinnedObject()) : default; 176 | return success; 177 | } 178 | finally 179 | { 180 | handle.Free(); 181 | } 182 | } 183 | 184 | 185 | [DllImport(dll, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 186 | private static extern bool DbgMemWrite(nuint va, IntPtr src, nuint size); 187 | 188 | public static unsafe bool DbgMemWrite(nuint va, T[] buffer, nuint size) where T : unmanaged 189 | { 190 | if (buffer is null || size > (nuint)buffer.Length * (nuint)sizeof(T)) 191 | return false; 192 | 193 | fixed (T* ptr = buffer) 194 | { 195 | return DbgMemWrite(va, (IntPtr)ptr, size); 196 | } 197 | } 198 | 199 | public static unsafe bool DbgMemWrite(nuint va, ref T src, nuint size) where T : struct 200 | { 201 | if (size > (nuint)Marshal.SizeOf()) 202 | return false; 203 | 204 | var handle = GCHandle.Alloc(src, GCHandleType.Pinned); 205 | try 206 | { 207 | return DbgMemWrite(va, handle.AddrOfPinnedObject(), size); 208 | } 209 | finally 210 | { 211 | handle.Free(); 212 | } 213 | } 214 | 215 | 216 | 217 | 218 | 219 | 220 | public const uint MEM_IMAGE = 0x1000000; // Memory type constant 221 | // Define MEMORY_BASIC_INFORMATION matching Windows API for the target platform 222 | [StructLayout(LayoutKind.Sequential, Pack = NativePacking)] 223 | public struct MEMORY_BASIC_INFORMATION 224 | { 225 | public IntPtr BaseAddress; 226 | public IntPtr AllocationBase; 227 | public uint AllocationProtect; // PROTECT_FLAGS enum 228 | #if AMD64 // PartitionId exists on 64-bit and >= Win8. Check if needed. 229 | public ushort PartitionId; 230 | // Packing might require explicit padding if PartitionId isn't always present or if alignment dictates 231 | // public ushort ReservedPadding; // Example 232 | #endif 233 | public nuint RegionSize; // SIZE_T maps to nuint 234 | public uint State; // MEM_STATE enum (e.g., MEM_COMMIT) 235 | public uint Protect; // PROTECT_FLAGS enum (e.g., PAGE_EXECUTE_READ) 236 | public uint Type; // MEM_TYPE enum (e.g., MEM_IMAGE) 237 | } 238 | 239 | // Define MEMPAGE matching C++ struct 240 | [StructLayout(LayoutKind.Sequential, Pack = NativePacking, CharSet = CharSet.Ansi)] 241 | public struct MEMPAGE 242 | { 243 | public MEMORY_BASIC_INFORMATION mbi; 244 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] // MAX_MODULE_SIZE = 256 245 | public string info; // This likely holds module path/name 246 | } 247 | 248 | // Define MEMMAP_NATIVE matching C++ MEMMAP struct 249 | [StructLayout(LayoutKind.Sequential, Pack = NativePacking)] 250 | public struct MEMMAP_NATIVE 251 | { 252 | public int count; // C++ uses int 253 | public IntPtr page; // C++ uses MEMPAGE* pointer 254 | } 255 | 256 | // --- P/Invoke Signatures --- 257 | 258 | [DllImport(dll, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 259 | public static extern bool DbgMemMap(ref MEMMAP_NATIVE memmap); // Use the native struct 260 | 261 | #if AMD64 // Define X64 symbol in your project properties for x64 builds 262 | public const int NativePacking = 16; 263 | public const bool Is64Bit = true; 264 | #else // Assuming x86 otherwise 265 | public const int NativePacking = 8; 266 | public const bool Is64Bit = false; 267 | #endif 268 | 269 | [StructLayout(LayoutKind.Sequential, Pack = NativePacking, CharSet = CharSet.Ansi)] 270 | public struct THREADINFO_NATIVE 271 | { 272 | public int ThreadNumber; 273 | public IntPtr Handle; // HANDLE maps to IntPtr (matches target architecture size) 274 | public uint ThreadId; // DWORD maps to uint 275 | 276 | #if AMD64 277 | public ulong ThreadStartAddress; // duint maps to ulong on x64 278 | public ulong ThreadLocalBase; // duint maps to ulong on x64 279 | #else 280 | public uint ThreadStartAddress; // duint maps to uint on x86 281 | public uint ThreadLocalBase; // duint maps to uint on x86 282 | #endif 283 | 284 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] // MAX_THREAD_NAME_SIZE = 256 285 | public string threadName; 286 | } 287 | 288 | [StructLayout(LayoutKind.Sequential, Pack = NativePacking)] 289 | public struct THREADALLINFO 290 | { 291 | public THREADINFO_NATIVE BasicInfo; 292 | 293 | #if AMD64 294 | public ulong ThreadCip; // duint maps to ulong on x64 295 | #else 296 | public uint ThreadCip; // duint maps to uint on x86 297 | #endif 298 | 299 | public uint SuspendCount; // DWORD maps to uint 300 | public int Priority; // THREADPRIORITY likely maps to int 301 | public int WaitReason; // THREADWAITREASON likely maps to int - CORRECT ORDER 302 | public uint LastError; // DWORD maps to uint - CORRECT ORDER 303 | public FILETIME UserTime; // CORRECT ORDER 304 | public FILETIME KernelTime; // CORRECT ORDER 305 | public FILETIME CreationTime; // CORRECT ORDER 306 | public ulong Cycles; // ULONG64 maps to ulong (always 64-bit) - CORRECT ORDER 307 | } 308 | 309 | [StructLayout(LayoutKind.Sequential, Pack = NativePacking)] 310 | public struct THREADLIST_NATIVE 311 | { 312 | public int count; 313 | public IntPtr list; // Correct order: pointer first 314 | public int CurrentThread; // Correct order: index second 315 | } 316 | 317 | // --- P/Invoke Signatures --- 318 | 319 | [DllImport(dll, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 320 | public static extern void DbgGetThreadList(ref THREADLIST_NATIVE list); 321 | 322 | 323 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 324 | public static extern bool DbgXrefGet(nuint addr, ref XREF_INFO info); 325 | 326 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 327 | public static extern bool DbgLoopAdd(nuint start, nuint end); 328 | 329 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 330 | public static extern bool DbgLoopGet(int depth, nuint addr, out nuint start, out nuint end); 331 | 332 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 333 | public static extern bool DbgLoopDel(int depth, nuint addr); 334 | 335 | public enum SEGMENTREG 336 | { 337 | SEG_DEFAULT, 338 | SEG_ES, 339 | SEG_DS, 340 | SEG_FS, 341 | SEG_GS, 342 | SEG_CS, 343 | SEG_SS 344 | } 345 | 346 | public enum DISASM_INSTRTYPE 347 | { 348 | instr_normal, 349 | instr_branch, 350 | instr_stack 351 | } 352 | 353 | public enum DISASM_ARGTYPE 354 | { 355 | arg_normal, 356 | arg_memory 357 | } 358 | 359 | public enum XREFTYPE 360 | { 361 | XREF_NONE, 362 | XREF_DATA, 363 | XREF_JMP, 364 | XREF_CALL 365 | } 366 | 367 | #region Definitions for BASIC_INSTRUCTION_INFO.type 368 | public const uint TYPE_VALUE = 1; 369 | public const uint TYPE_MEMORY = 2; 370 | public const uint TYPE_ADDR = 4; 371 | #endregion 372 | 373 | public enum MEMORY_SIZE 374 | { 375 | size_byte = 1, 376 | size_word = 2, 377 | size_dword = 4, 378 | size_qword = 8, 379 | size_xmmword = 16, 380 | size_ymmword = 32 381 | } 382 | 383 | [Serializable] 384 | public struct VALUE_INFO 385 | { 386 | public nuint value; 387 | public MEMORY_SIZE size; 388 | } 389 | 390 | [Serializable] 391 | public unsafe struct MEMORY_INFO 392 | { 393 | public nuint value; //displacement / addrvalue (rip-relative) 394 | public MEMORY_SIZE size; //byte/word/dword/qword 395 | 396 | private fixed byte mnemonicBytes[MAX_MNEMONIC_SIZE]; 397 | public string mnemonic 398 | { 399 | get 400 | { 401 | fixed (byte* ptr = mnemonicBytes) 402 | return new IntPtr(ptr).MarshalToStringUTF8(MAX_MNEMONIC_SIZE); 403 | } 404 | } 405 | } 406 | 407 | [Serializable] 408 | public unsafe struct BASIC_INSTRUCTION_INFO 409 | { 410 | public uint type; //value|memory|addr 411 | public VALUE_INFO value; //immediat 412 | public MEMORY_INFO memory; 413 | public nuint addr; //addrvalue (jumps + calls) 414 | public BlittableBoolean branch; //jumps/calls 415 | public BlittableBoolean call; //instruction is a call 416 | 417 | public int size; 418 | 419 | private fixed byte instructionBytes[MAX_MNEMONIC_SIZE * 4]; 420 | public string instruction 421 | { 422 | get 423 | { 424 | fixed (byte* ptr = instructionBytes) 425 | return new IntPtr(ptr).MarshalToStringUTF8(MAX_MNEMONIC_SIZE * 4); 426 | } 427 | set 428 | { 429 | fixed (byte* ptr = instructionBytes) 430 | value.MarshalToPtrUTF8(new IntPtr(ptr), MAX_MNEMONIC_SIZE * 4); 431 | } 432 | } 433 | } 434 | 435 | [Serializable] 436 | public unsafe struct DISASM_ARG 437 | { 438 | public DISASM_ARGTYPE type; 439 | public SEGMENTREG segment; 440 | private fixed byte _mnemonic[MAX_MNEMONIC_SIZE]; 441 | public string mnemonic 442 | { 443 | get 444 | { 445 | fixed (byte* ptr = _mnemonic) 446 | return new IntPtr(ptr).MarshalToStringUTF8(MAX_MNEMONIC_SIZE); 447 | } 448 | } 449 | public nuint constant; 450 | public nuint value; 451 | public nuint memvalue; 452 | } 453 | 454 | [Serializable] 455 | public unsafe struct DISASM_INSTR 456 | { 457 | private fixed byte _instruction[MAX_MNEMONIC_SIZE]; 458 | public string instruction 459 | { 460 | get 461 | { 462 | fixed (byte* ptr = _instruction) 463 | return new IntPtr(ptr).MarshalToStringUTF8(MAX_MNEMONIC_SIZE); 464 | } 465 | } 466 | public DISASM_INSTRTYPE type; 467 | public int argcount; 468 | public int instr_size; 469 | 470 | public DISASM_ARG arg0; // Maps to arg[0] 471 | public DISASM_ARG arg1; // Maps to arg[1] 472 | public DISASM_ARG arg2; // Maps to arg[2] 473 | } 474 | 475 | [Serializable] 476 | public unsafe struct XREF_INFO 477 | { 478 | public nuint refcount; 479 | 480 | private XREF_RECORD* _references; 481 | public XREF_RECORD[] references 482 | { 483 | get 484 | { 485 | if (_references == null || refcount == UIntPtr.Zero) 486 | return new XREF_RECORD[0]; 487 | 488 | var result = new XREF_RECORD[(int)refcount]; 489 | for (int i = 0; i < (int)refcount; i++) 490 | { 491 | result[i] = _references[i]; 492 | } 493 | 494 | return result; 495 | } 496 | } 497 | } 498 | 499 | [Serializable] 500 | public unsafe struct XREF_RECORD 501 | { 502 | public nuint addr; 503 | public XREFTYPE type; 504 | } 505 | } 506 | } 507 | --------------------------------------------------------------------------------