├── .editorconfig ├── .gitignore ├── Directory.Build.props ├── DotNetPlugin.Impl ├── DotNetPlugin.Impl.csproj ├── ILRepack.targets ├── NativeBindings │ ├── SDK │ │ ├── Bridge.Dbg.cs │ │ ├── Bridge.Gui.cs │ │ ├── Bridge.cs │ │ └── TitanEngine.cs │ ├── Script │ │ ├── Argument.cs │ │ ├── Disassembly.cs │ │ ├── Gui.cs │ │ ├── Module.cs │ │ ├── Pattern.cs │ │ └── Register.cs │ └── Win32 │ │ ├── Constants.cs │ │ ├── Functions.Kernel32.cs │ │ ├── Functions.Psapi.cs │ │ └── Types.cs ├── Plugin.Commands.cs ├── Plugin.EventCallbacks.cs ├── Plugin.ExpressionFunctions.cs ├── Plugin.Menus.cs ├── Plugin.cs ├── Properties │ ├── Resources.Designer.cs │ └── Resources.resx └── Resources │ └── abouticon.png ├── DotNetPlugin.RemotingHelper ├── AppDomainInitializer.cs └── DotNetPlugin.RemotingHelper.csproj ├── DotNetPlugin.Stub ├── Commands.cs ├── DotNetPlugin.Stub.csproj ├── EventCallbacks.cs ├── ExpressionFunctions.cs ├── IPlugin.cs ├── IPluginSession.cs ├── Menus.cs ├── NativeBindings │ ├── BlittableBoolean.cs │ ├── Extensions.cs │ ├── SDK │ │ ├── Bridge.cs │ │ ├── PLog.cs │ │ └── Plugins.cs │ ├── StructRef.cs │ ├── Utf8StringRef.cs │ └── Win32 │ │ ├── Types.DebugEvents.cs │ │ ├── Types.WinGdi.cs │ │ ├── Types.cs │ │ └── Win32Window.cs ├── PluginBase.cs ├── PluginMain.cs ├── PluginSession.cs └── PluginSessionProxy.cs ├── DotNetPluginCS.sln └── README.md /.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 -------------------------------------------------------------------------------- /.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/ -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | x64dbgDotNetPlugin 4 | DotNetPlugin 5 | 6 | DotNetPlugin 7 | ..\bin\$(Platform)\$(Configuration)\ 8 | 9 | 9 10 | 11 | 12 | 13 | true 14 | 15 | 16 | -------------------------------------------------------------------------------- /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 | 14 | 15 | 16 | X86;$(DefineConstants) 17 | .dp32 18 | 19 | 20 | AMD64;$(DefineConstants) 21 | .dp64 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | build 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | True 49 | True 50 | Resources.resx 51 | 52 | 53 | 54 | 55 | 56 | ResXFileCodeGenerator 57 | Resources.Designer.cs 58 | 59 | 60 | 61 | 71 | 72 | 73 | 74 | $([System.Text.RegularExpressions.Regex]::Replace($(TargetName), '\.Impl$', '')) 75 | .dll 76 | $(TargetDir)$(StubAssemblyName)$(StubAssemblyExt) 77 | $(StubAssemblyPath) 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/ILRepack.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/NativeBindings/SDK/Bridge.Dbg.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 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 10 | public static extern bool DbgCmdExec([MarshalAs(UnmanagedType.LPUTF8Str)] string cmd); 11 | 12 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 13 | public static extern bool DbgCmdExecDirect([MarshalAs(UnmanagedType.LPUTF8Str)] string cmd); 14 | 15 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 16 | public static extern void DbgDisasmFastAt(nuint addr, ref BASIC_INSTRUCTION_INFO basicinfo); 17 | 18 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 19 | public static extern nuint DbgGetBranchDestination(nuint addr); 20 | 21 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 22 | private static extern bool DbgGetCommentAt(nuint addr, IntPtr text); 23 | 24 | public static unsafe bool DbgGetCommentAt(nuint addr, out string text) 25 | { 26 | var textBufferPtr = stackalloc byte[MAX_COMMENT_SIZE]; 27 | var success = DbgGetCommentAt(addr, new IntPtr(textBufferPtr)); 28 | text = success ? new IntPtr(textBufferPtr).MarshalToStringUTF8(MAX_COMMENT_SIZE) : default; 29 | return success; 30 | } 31 | 32 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 33 | private static extern bool DbgGetLabelAt(nuint addr, SEGMENTREG segment, IntPtr text); 34 | 35 | public static unsafe bool DbgGetLabelAt(nuint addr, SEGMENTREG segment, out string text) 36 | { 37 | var textBufferPtr = stackalloc byte[MAX_LABEL_SIZE]; 38 | var success = DbgGetLabelAt(addr, segment, new IntPtr(textBufferPtr)); 39 | text = success ? new IntPtr(textBufferPtr).MarshalToStringUTF8(MAX_LABEL_SIZE) : default; 40 | return success; 41 | } 42 | 43 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 44 | private static extern bool DbgGetModuleAt(nuint addr, IntPtr text); 45 | 46 | public static unsafe bool DbgGetModuleAt(nuint addr, out string text) 47 | { 48 | var textBufferPtr = stackalloc byte[MAX_MODULE_SIZE]; 49 | var success = DbgGetModuleAt(addr, new IntPtr(textBufferPtr)); 50 | text = success ? new IntPtr(textBufferPtr).MarshalToStringUTF8(MAX_MODULE_SIZE) : default; 51 | return success; 52 | } 53 | 54 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 55 | public static extern bool DbgIsDebugging(); 56 | 57 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 58 | public static extern nuint DbgModBaseFromName([MarshalAs(UnmanagedType.LPUTF8Str)] string name); 59 | 60 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 61 | public static extern nuint DbgValFromString([MarshalAs(UnmanagedType.LPUTF8Str)] string @string); 62 | 63 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 64 | public static extern bool DbgValToString([MarshalAs(UnmanagedType.LPUTF8Str)] string @string, nuint value); 65 | 66 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 67 | public static extern void DbgDisasmAt(nuint addr, ref DISASM_INSTR instr); 68 | 69 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 70 | public static extern nuint DbgMemFindBaseAddr(nuint addr, out nuint size); 71 | 72 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 73 | public static extern bool DbgSetCommentAt(nuint addr, [MarshalAs(UnmanagedType.LPUTF8Str)] string text); 74 | 75 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 76 | public static extern bool DbgSetLabelAt(nuint addr, [MarshalAs(UnmanagedType.LPUTF8Str)] string text); 77 | 78 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 79 | public static extern bool DbgSetAutoCommentAt(nuint addr, [MarshalAs(UnmanagedType.LPUTF8Str)] string text); 80 | 81 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 82 | public static extern bool DbgSetAutoLabelAt(nuint addr, [MarshalAs(UnmanagedType.LPUTF8Str)] string text); 83 | 84 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 85 | public static extern void DbgClearAutoCommentRange(nuint start, nuint end); 86 | 87 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 88 | public static extern void DbgClearAutoLabelRange(nuint start, nuint end); 89 | 90 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 91 | public static extern void DbgClearCommentRange(nuint start, nuint end); 92 | 93 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 94 | public static extern void DbgClearLabelRange(nuint start, nuint end); 95 | 96 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 97 | private static extern bool DbgMemRead(nuint va, IntPtr dest, nuint size); 98 | 99 | public static unsafe bool DbgMemRead(nuint va, T[] buffer, nuint size) where T : unmanaged 100 | { 101 | if (buffer is null || size > (nuint)buffer.Length) return false; 102 | 103 | fixed (T* ptr = buffer) 104 | { 105 | return DbgMemRead(va, (IntPtr)ptr, size); 106 | } 107 | } 108 | 109 | public static unsafe bool DbgMemRead(nuint va, ref T dest, nuint size) where T : struct 110 | { 111 | if (size > (nuint)Marshal.SizeOf(dest)) return false; 112 | 113 | var handle = GCHandle.Alloc(dest, GCHandleType.Pinned); 114 | try 115 | { 116 | var success = DbgMemRead(va, handle.AddrOfPinnedObject(), size); 117 | dest = success ? Marshal.PtrToStructure(handle.AddrOfPinnedObject()) : default; 118 | return success; 119 | } 120 | finally 121 | { 122 | handle.Free(); 123 | } 124 | } 125 | 126 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 127 | public static extern bool DbgXrefGet(nuint addr, ref XREF_INFO info); 128 | 129 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 130 | public static extern bool DbgLoopAdd(nuint start, nuint end); 131 | 132 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 133 | public static extern bool DbgLoopGet(int depth, nuint addr, out nuint start, out nuint end); 134 | 135 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 136 | public static extern bool DbgLoopDel(int depth, nuint addr); 137 | 138 | public enum SEGMENTREG 139 | { 140 | SEG_DEFAULT, 141 | SEG_ES, 142 | SEG_DS, 143 | SEG_FS, 144 | SEG_GS, 145 | SEG_CS, 146 | SEG_SS 147 | } 148 | 149 | public enum DISASM_INSTRTYPE 150 | { 151 | instr_normal, 152 | instr_branch, 153 | instr_stack 154 | } 155 | 156 | public enum DISASM_ARGTYPE 157 | { 158 | arg_normal, 159 | arg_memory 160 | } 161 | 162 | public enum XREFTYPE 163 | { 164 | XREF_NONE, 165 | XREF_DATA, 166 | XREF_JMP, 167 | XREF_CALL 168 | } 169 | 170 | #region Definitions for BASIC_INSTRUCTION_INFO.type 171 | public const uint TYPE_VALUE = 1; 172 | public const uint TYPE_MEMORY = 2; 173 | public const uint TYPE_ADDR = 4; 174 | #endregion 175 | 176 | public enum MEMORY_SIZE 177 | { 178 | size_byte = 1, 179 | size_word = 2, 180 | size_dword = 4, 181 | size_qword = 8, 182 | size_xmmword = 16, 183 | size_ymmword = 32 184 | } 185 | 186 | [Serializable] 187 | public struct VALUE_INFO 188 | { 189 | public nuint value; 190 | public MEMORY_SIZE size; 191 | } 192 | 193 | [Serializable] 194 | public unsafe struct MEMORY_INFO 195 | { 196 | public nuint value; //displacement / addrvalue (rip-relative) 197 | public MEMORY_SIZE size; //byte/word/dword/qword 198 | 199 | private fixed byte mnemonicBytes[MAX_MNEMONIC_SIZE]; 200 | public string mnemonic 201 | { 202 | get 203 | { 204 | fixed (byte* ptr = mnemonicBytes) 205 | return new IntPtr(ptr).MarshalToStringUTF8(MAX_MNEMONIC_SIZE); 206 | } 207 | } 208 | } 209 | 210 | [Serializable] 211 | public unsafe struct BASIC_INSTRUCTION_INFO 212 | { 213 | public uint type; //value|memory|addr 214 | public VALUE_INFO value; //immediat 215 | public MEMORY_INFO memory; 216 | public nuint addr; //addrvalue (jumps + calls) 217 | public BlittableBoolean branch; //jumps/calls 218 | public BlittableBoolean call; //instruction is a call 219 | 220 | public int size; 221 | 222 | private fixed byte instructionBytes[MAX_MNEMONIC_SIZE * 4]; 223 | public string instruction 224 | { 225 | get 226 | { 227 | fixed (byte* ptr = instructionBytes) 228 | return new IntPtr(ptr).MarshalToStringUTF8(MAX_MNEMONIC_SIZE * 4); 229 | } 230 | set 231 | { 232 | fixed (byte* ptr = instructionBytes) 233 | value.MarshalToPtrUTF8(new IntPtr(ptr), MAX_MNEMONIC_SIZE * 4); 234 | } 235 | } 236 | } 237 | 238 | [Serializable] 239 | public unsafe struct DISASM_ARG 240 | { 241 | public DISASM_ARGTYPE type; 242 | public SEGMENTREG segment; 243 | private fixed byte _mnemonic[MAX_MNEMONIC_SIZE]; 244 | public string mnemonic 245 | { 246 | get 247 | { 248 | fixed (byte* ptr = _mnemonic) 249 | return new IntPtr(ptr).MarshalToStringUTF8(MAX_MNEMONIC_SIZE); 250 | } 251 | } 252 | public nuint constant; 253 | public nuint value; 254 | public nuint memvalue; 255 | } 256 | 257 | [Serializable] 258 | public unsafe struct DISASM_INSTR 259 | { 260 | private fixed byte _instruction[MAX_MNEMONIC_SIZE]; 261 | public string instruction 262 | { 263 | get 264 | { 265 | fixed (byte* ptr = _instruction) 266 | return new IntPtr(ptr).MarshalToStringUTF8(MAX_MNEMONIC_SIZE); 267 | } 268 | } 269 | public DISASM_INSTRTYPE type; 270 | public int argcount; 271 | public int instr_size; 272 | 273 | public DISASM_ARG arg0; // Maps to arg[0] 274 | public DISASM_ARG arg1; // Maps to arg[1] 275 | public DISASM_ARG arg2; // Maps to arg[2] 276 | } 277 | 278 | [Serializable] 279 | public unsafe struct XREF_INFO 280 | { 281 | public nuint refcount; 282 | 283 | private XREF_RECORD* _references; 284 | public XREF_RECORD[] references 285 | { 286 | get 287 | { 288 | if (_references == null || refcount == UIntPtr.Zero) 289 | return new XREF_RECORD[0]; 290 | 291 | var result = new XREF_RECORD[(int)refcount]; 292 | for (int i = 0; i < (int)refcount; i++) 293 | { 294 | result[i] = _references[i]; 295 | } 296 | 297 | return result; 298 | } 299 | } 300 | } 301 | 302 | [Serializable] 303 | public unsafe struct XREF_RECORD 304 | { 305 | public nuint addr; 306 | public XREFTYPE type; 307 | } 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /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.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 | -------------------------------------------------------------------------------- /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/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.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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.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.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.Impl/Plugin.Commands.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Windows.Forms; 4 | using DotNetPlugin.NativeBindings; 5 | using DotNetPlugin.NativeBindings.Script; 6 | using DotNetPlugin.NativeBindings.SDK; 7 | using Microsoft.VisualBasic; 8 | using Microsoft.VisualBasic.CompilerServices; 9 | 10 | namespace DotNetPlugin 11 | { 12 | partial class Plugin 13 | { 14 | [Command("DotNetpluginTestCommand")] 15 | public static void cbNetTestCommand(string[] args) 16 | { 17 | Console.WriteLine(".Net test command!"); 18 | string empty = string.Empty; 19 | string Left = Interaction.InputBox("Enter value pls", "NetTest", "", -1, -1); 20 | if (Left == null | Operators.CompareString(Left, "", false) == 0) 21 | Console.WriteLine("cancel pressed!"); 22 | else 23 | Console.WriteLine($"line: {Left}"); 24 | } 25 | 26 | [Command("DotNetDumpProcess", DebugOnly = true)] 27 | public static bool cbDumpProcessCommand(string[] args) 28 | { 29 | var addr = args.Length >= 2 ? Bridge.DbgValFromString(args[1]) : Bridge.DbgValFromString("cip"); 30 | Console.WriteLine($"addr: {addr.ToPtrString()}"); 31 | var modinfo = new Module.ModuleInfo(); 32 | if (!Module.InfoFromAddr(addr, ref modinfo)) 33 | { 34 | Console.Error.WriteLine($"Module.InfoFromAddr failed..."); 35 | return false; 36 | } 37 | Console.WriteLine($"InfoFromAddr success, base: {modinfo.@base.ToPtrString()}"); 38 | var hProcess = Bridge.DbgValFromString("$hProcess"); 39 | var saveFileDialog = new SaveFileDialog 40 | { 41 | Filter = "Executables (*.dll,*.exe)|*.exe|All Files (*.*)|*.*", 42 | RestoreDirectory = true, 43 | FileName = modinfo.name 44 | }; 45 | using (saveFileDialog) 46 | { 47 | var result = DialogResult.Cancel; 48 | var t = new Thread(() => result = saveFileDialog.ShowDialog()); 49 | t.SetApartmentState(ApartmentState.STA); 50 | t.Start(); 51 | t.Join(); 52 | if (result == DialogResult.OK) 53 | { 54 | string fileName = saveFileDialog.FileName; 55 | if (!TitanEngine.DumpProcess((nint)hProcess, (nint)modinfo.@base, fileName, addr)) 56 | { 57 | Console.Error.WriteLine($"DumpProcess failed..."); 58 | return false; 59 | } 60 | Console.WriteLine($"Dumping done!"); 61 | } 62 | } 63 | return true; 64 | } 65 | 66 | [Command("DotNetModuleEnum", DebugOnly = true)] 67 | public static void cbModuleEnum(string[] args) 68 | { 69 | foreach (var mod in Module.GetList()) 70 | { 71 | Console.WriteLine($"{mod.@base.ToPtrString()} {mod.name}"); 72 | foreach (var section in Module.SectionListFromAddr(mod.@base)) 73 | Console.WriteLine($" {section.addr.ToPtrString()} \"{section.name}\""); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/Plugin.EventCallbacks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DotNetPlugin.NativeBindings; 3 | using DotNetPlugin.NativeBindings.SDK; 4 | 5 | namespace DotNetPlugin 6 | { 7 | partial class Plugin 8 | { 9 | [EventCallback(Plugins.CBTYPE.CB_INITDEBUG)] 10 | public static void OnInitDebug(ref Plugins.PLUG_CB_INITDEBUG info) 11 | { 12 | var szFileName = info.szFileName; 13 | LogInfo($"DotNet test debugging of file {szFileName} started!"); 14 | } 15 | 16 | [EventCallback(Plugins.CBTYPE.CB_STOPDEBUG)] 17 | public static void OnStopDebug(ref Plugins.PLUG_CB_STOPDEBUG info) 18 | { 19 | LogInfo($"DotNet test debugging stopped!"); 20 | } 21 | 22 | [EventCallback(Plugins.CBTYPE.CB_CREATEPROCESS)] 23 | public static void OnCreateProcess(IntPtr infoPtr) 24 | { 25 | // info can also be cast manually 26 | var info = infoPtr.ToStructUnsafe(); 27 | 28 | var CreateProcessInfo = info.CreateProcessInfo; 29 | var modInfo = info.modInfo; 30 | string DebugFileName = info.DebugFileName; 31 | var fdProcessInfo = info.fdProcessInfo; 32 | LogInfo($"Create process {info.DebugFileName}"); 33 | } 34 | 35 | [EventCallback(Plugins.CBTYPE.CB_LOADDLL)] 36 | public static void OnLoadDll(ref Plugins.PLUG_CB_LOADDLL info) 37 | { 38 | var LoadDll = info.LoadDll; 39 | var modInfo = info.modInfo; 40 | string modname = info.modname; 41 | LogInfo($"Load DLL {modname}"); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /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/Plugin.Menus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using DotNetPlugin.NativeBindings.SDK; 4 | using DotNetPlugin.Properties; 5 | 6 | namespace DotNetPlugin 7 | { 8 | partial class Plugin 9 | { 10 | protected override void SetupMenu(Menus menus) 11 | { 12 | menus.Main 13 | .AddAndConfigureItem("&About...", OnAboutMenuItem).SetIcon(Resources.AboutIcon).Parent 14 | .AddAndConfigureItem("&DotNetDumpProcess", OnDumpMenuItem).SetHotKey("CTRL+F12").Parent 15 | .AddAndConfigureSubMenu("sub menu") 16 | .AddItem("sub menu entry1", menuItem => Console.WriteLine($"hEntry={menuItem.Id}")) 17 | .AddSeparator() 18 | .AddItem("sub menu entry2", menuItem => Console.WriteLine($"hEntry={menuItem.Id}")); 19 | } 20 | 21 | public void OnAboutMenuItem(MenuItem menuItem) 22 | { 23 | MessageBox.Show(HostWindow, "DotNet Plugin For x64dbg\nCoded By ", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); 24 | } 25 | 26 | public static void OnDumpMenuItem(MenuItem menuItem) 27 | { 28 | if (!Bridge.DbgIsDebugging()) 29 | { 30 | Console.WriteLine("You need to be debugging to use this Command"); 31 | return; 32 | } 33 | Bridge.DbgCmdExec("DotNetDumpProcess"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /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 | // You can listen to debugger events in two ways: 24 | // 1. by declaring dll exports in the Stub project (see PluginMain), then adding the corresponding methods to the IPlugin interface, 25 | // finally implementing them as required to propagate the call to the Plugin class or 26 | // 2. by registering callbacks using the EventCallback attribute (see Plugin.EventCallbacks.cs). 27 | 28 | // Please note that Option 1 goes through remoting in Debug builds (where Impl assembly unloading is enabled), 29 | // 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. 30 | 31 | // Commands and function expressions are discovered and registered automatically. See Plugin.Commands.cs and Plugin.ExpressionFunctions.cs. 32 | 33 | // Menus can be registered by overriding the SetupMenu method. See Plugin.Menus.cs. 34 | 35 | return true; 36 | } 37 | 38 | public override void Setup(ref Plugins.PLUG_SETUPSTRUCT setupStruct) 39 | { 40 | // Do additional UI setup (apart from menus) here. 41 | } 42 | 43 | public override Task StopAsync() 44 | { 45 | // Do additional cleanup here. 46 | 47 | return Task.FromResult(true); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\resources\abouticon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | -------------------------------------------------------------------------------- /DotNetPlugin.Impl/Resources/abouticon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrexodia/DotNetPluginCS/d1f367318d2d29d2c7c2c66e7a8d062eeebc6874/DotNetPlugin.Impl/Resources/abouticon.png -------------------------------------------------------------------------------- /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.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.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 | 18 | public bool DebugOnly { get; set; } 19 | 20 | public CommandAttribute() { } 21 | 22 | public CommandAttribute(string name) 23 | { 24 | Name = name; 25 | } 26 | } 27 | 28 | internal static class Commands 29 | { 30 | private static Plugins.CBPLUGINCOMMAND BuildCallback(PluginBase plugin, MethodInfo method, bool reportsSuccess) 31 | { 32 | object firstArg = method.IsStatic ? null : plugin; 33 | 34 | if (reportsSuccess) 35 | { 36 | return (Plugins.CBPLUGINCOMMAND)Delegate.CreateDelegate(typeof(Plugins.CBPLUGINCOMMAND), firstArg, method, throwOnBindFailure: true); 37 | } 38 | else 39 | { 40 | var callback = (Action)Delegate.CreateDelegate(typeof(Action), firstArg, method, throwOnBindFailure: true); 41 | return args => 42 | { 43 | callback(args); 44 | return true; 45 | }; 46 | } 47 | } 48 | 49 | public static IDisposable Initialize(PluginBase plugin, MethodInfo[] pluginMethods) 50 | { 51 | // command names are case-insensitive 52 | var registeredNames = new HashSet(StringComparer.OrdinalIgnoreCase); 53 | 54 | var methods = pluginMethods 55 | .SelectMany(method => method.GetCustomAttributes().Select(attribute => (method, attribute))); 56 | 57 | foreach (var (method, attribute) in methods) 58 | { 59 | var name = attribute.Name ?? method.Name; 60 | 61 | var reportsSuccess = method.ReturnType == typeof(bool); 62 | if (!reportsSuccess && method.ReturnType != typeof(void)) 63 | { 64 | PluginBase.LogError($"Registration of command '{name}' is skipped. Method '{method.Name}' has an invalid return type."); 65 | continue; 66 | } 67 | 68 | var methodParams = method.GetParameters(); 69 | 70 | if (methodParams.Length != 1 || methodParams[0].ParameterType != typeof(string[])) 71 | { 72 | PluginBase.LogError($"Registration of command '{name}' is skipped. Method '{method.Name}' has an invalid signature."); 73 | continue; 74 | } 75 | 76 | if (registeredNames.Contains(name) || 77 | !Plugins._plugin_registercommand(plugin.PluginHandle, name, BuildCallback(plugin, method, reportsSuccess), attribute.DebugOnly)) 78 | { 79 | PluginBase.LogError($"Registration of command '{name}' failed."); 80 | continue; 81 | } 82 | 83 | registeredNames.Add(name); 84 | } 85 | 86 | return new Registrations(plugin, registeredNames); 87 | } 88 | 89 | private sealed class Registrations : IDisposable 90 | { 91 | private PluginBase _plugin; 92 | private HashSet _registeredNames; 93 | 94 | public Registrations(PluginBase plugin, HashSet registeredNames) 95 | { 96 | _plugin = plugin; 97 | _registeredNames = registeredNames; 98 | } 99 | 100 | public void Dispose() 101 | { 102 | var plugin = Interlocked.Exchange(ref _plugin, null); 103 | 104 | if (plugin != null) 105 | { 106 | foreach (var name in _registeredNames) 107 | { 108 | if (!Plugins._plugin_unregistercommand(plugin.PluginHandle, name)) 109 | PluginBase.LogError($"Unregistration of command '{name}' failed."); 110 | } 111 | 112 | _registeredNames = null; 113 | } 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /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 | build.meta 44 | $(IntermediateOutputPath)$(BuildMetadataFileName) 45 | $([System.IO.Path]::GetFullPath($(PluginOutputPath)))$(TargetName).Impl$(TargetExt) 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | $(BuildMetadataFileName) 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | build 66 | 67 | 68 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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.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/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.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/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/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.Stub/NativeBindings/SDK/Plugins.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using DotNetPlugin.NativeBindings.Win32; 5 | 6 | namespace DotNetPlugin.NativeBindings.SDK 7 | { 8 | // https://github.com/x64dbg/x64dbg/blob/development/src/dbg/_plugins.h 9 | public static class Plugins 10 | { 11 | public const int PLUG_SDKVERSION = 1; 12 | 13 | public delegate void CBPLUGIN(CBTYPE cbType, IntPtr callbackInfo); 14 | 15 | public delegate bool CBPLUGINCOMMAND(string[] args); 16 | 17 | public delegate nuint CBPLUGINEXPRFUNCTION(nuint[] args, object userdata); 18 | public delegate nuint CBPLUGINEXPRFUNCTION_RAWARGS(int argc, IntPtr argv, object userdata); 19 | 20 | #if AMD64 21 | private const string dll = "x64dbg.dll"; 22 | #else 23 | private const string dll = "x32dbg.dll"; 24 | #endif 25 | private const CallingConvention cdecl = CallingConvention.Cdecl; 26 | 27 | #region Logging 28 | 29 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 30 | public static extern void _plugin_logprint([MarshalAs(UnmanagedType.LPUTF8Str)] string text); 31 | 32 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 33 | public static extern void _plugin_logputs([MarshalAs(UnmanagedType.LPUTF8Str)] string text); 34 | 35 | #endregion 36 | 37 | #region Event Callbacks 38 | 39 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 40 | private delegate void CBPLUGIN_NATIVE(CBTYPE cbType, IntPtr callbackInfo); 41 | 42 | private static CBPLUGIN_NATIVE[] _pluginCallbacks = new CBPLUGIN_NATIVE[(int)CBTYPE.CB_LAST]; 43 | 44 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = nameof(_plugin_registercallback), ExactSpelling = true)] 45 | private static extern void _plugin_registercallback_native(int pluginHandle, CBTYPE cbType, CBPLUGIN_NATIVE cbPlugin); 46 | 47 | public static void _plugin_registercallback(int pluginHandle, CBTYPE cbType, CBPLUGIN cbPlugin) 48 | { 49 | if (cbPlugin == null) 50 | throw new ArgumentNullException(nameof(cbPlugin)); 51 | 52 | if (cbType < 0 || _pluginCallbacks.Length <= (int)cbType) 53 | throw new ArgumentOutOfRangeException(nameof(cbType)); 54 | 55 | CBPLUGIN_NATIVE callback = (cbType, callbackInfo) => 56 | { 57 | try 58 | { 59 | cbPlugin(cbType, callbackInfo); 60 | } 61 | catch (Exception ex) 62 | { 63 | PluginMain.LogUnhandledException(ex); 64 | } 65 | }; 66 | 67 | lock (_pluginCallbacks) 68 | { 69 | // The CLR protects the delegate from being GC'd only for the duration of the call, so we need to keep a reference to it until unregistration. 70 | _pluginCallbacks[(int)cbType] = callback; 71 | 72 | _plugin_registercallback_native(pluginHandle, cbType, callback); 73 | } 74 | } 75 | 76 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = nameof(_plugin_unregistercallback), ExactSpelling = true)] 77 | private static extern bool _plugin_unregistercallback_native(int pluginHandle, CBTYPE cbType); 78 | 79 | public static bool _plugin_unregistercallback(int pluginHandle, CBTYPE cbType) 80 | { 81 | if (cbType < 0 || _pluginCallbacks.Length <= (int)cbType) 82 | throw new ArgumentOutOfRangeException(nameof(cbType)); 83 | 84 | lock (_pluginCallbacks) 85 | { 86 | var success = _plugin_unregistercallback_native(pluginHandle, cbType); 87 | 88 | if (success) 89 | _pluginCallbacks[(int)cbType] = null; 90 | 91 | return success; 92 | } 93 | } 94 | 95 | #endregion 96 | 97 | #region Commands 98 | 99 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 100 | private delegate bool CBPLUGINCOMMAND_NATIVE(int argc, IntPtr argv); 101 | 102 | private static Dictionary _commandCallbacks = new Dictionary(); 103 | 104 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = nameof(_plugin_registercommand), ExactSpelling = true)] 105 | private static extern bool _plugin_registercommand_native(int pluginHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string command, CBPLUGINCOMMAND_NATIVE cbCommand, bool debugonly); 106 | 107 | public static bool _plugin_registercommand(int pluginHandle, string command, CBPLUGINCOMMAND cbCommand, bool debugonly) 108 | { 109 | if (command == null) 110 | throw new ArgumentNullException(nameof(command)); 111 | 112 | if (cbCommand == null) 113 | throw new ArgumentNullException(nameof(cbCommand)); 114 | 115 | CBPLUGINCOMMAND_NATIVE callback = (argc, argv) => 116 | { 117 | try 118 | { 119 | string[] argvArray; 120 | if (argc > 0) 121 | { 122 | argvArray = new string[argc]; 123 | for (int i = 0, ofs = 0; i < argvArray.Length; i++, ofs += IntPtr.Size) 124 | argvArray[i] = Marshal.ReadIntPtr(argv, ofs).MarshalToStringUTF8(); 125 | } 126 | else 127 | argvArray = Array.Empty(); 128 | 129 | return cbCommand(argvArray); 130 | } 131 | catch (Exception ex) 132 | { 133 | PluginMain.LogUnhandledException(ex); 134 | return false; 135 | } 136 | }; 137 | 138 | lock (_commandCallbacks) 139 | { 140 | // The CLR protects the delegate from being GC'd only for the duration of the call, so we need to keep a reference to it until unregistration. 141 | var success = _plugin_registercommand_native(pluginHandle, command, callback, debugonly); 142 | 143 | if (success) 144 | _commandCallbacks[command] = callback; 145 | 146 | return success; 147 | } 148 | } 149 | 150 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = nameof(_plugin_unregistercommand), ExactSpelling = true)] 151 | private static extern bool _plugin_unregistercommand_native(int pluginHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string command); 152 | 153 | public static bool _plugin_unregistercommand(int pluginHandle, string command) 154 | { 155 | if (command == null) 156 | throw new ArgumentNullException(nameof(command)); 157 | 158 | lock (_commandCallbacks) 159 | { 160 | var success = _plugin_unregistercommand_native(pluginHandle, command); 161 | 162 | if (success) 163 | _commandCallbacks.Remove(command); 164 | 165 | return success; 166 | } 167 | } 168 | 169 | #endregion 170 | 171 | #region Expression Functions 172 | 173 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 174 | private delegate nuint CBPLUGINEXPRFUNCTION_NATIVE(int argc, IntPtr argv, IntPtr userdata); 175 | 176 | private static Dictionary _expressionFunctionCallbacks = new Dictionary(); 177 | 178 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = nameof(_plugin_registerexprfunction), ExactSpelling = true)] 179 | private static extern bool _plugin_registerexprfunction_native(int pluginHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string name, int argc, 180 | CBPLUGINEXPRFUNCTION_NATIVE cbCommand, IntPtr userdata); 181 | 182 | private static bool _plugin_registerexprfunction_core(int pluginHandle, string name, int argc, CBPLUGINEXPRFUNCTION_NATIVE callback, object userdata) 183 | { 184 | var userdataHandle = userdata != null ? GCHandle.Alloc(userdata) : default; 185 | try 186 | { 187 | lock (_expressionFunctionCallbacks) 188 | { 189 | // The CLR protects the delegate from being GC'd only for the duration of the call, so we need to keep a reference to it until unregistration. 190 | var success = _plugin_registerexprfunction_native(pluginHandle, name, argc, callback, GCHandle.ToIntPtr(userdataHandle)); 191 | 192 | if (success) 193 | _expressionFunctionCallbacks[name] = (callback, userdataHandle); 194 | 195 | return success; 196 | } 197 | } 198 | catch 199 | { 200 | if (userdataHandle.IsAllocated) 201 | userdataHandle.Free(); 202 | throw; 203 | } 204 | } 205 | 206 | public static bool _plugin_registerexprfunction(int pluginHandle, string name, int argc, CBPLUGINEXPRFUNCTION cbFunction, object userdata) 207 | { 208 | if (name == null) 209 | throw new ArgumentNullException(nameof(name)); 210 | 211 | if (cbFunction == null) 212 | throw new ArgumentNullException(nameof(cbFunction)); 213 | 214 | CBPLUGINEXPRFUNCTION_NATIVE callback = (argc, argv, userdata) => 215 | { 216 | try 217 | { 218 | nuint[] argvArray; 219 | if (argc > 0) 220 | { 221 | argvArray = new nuint[argc]; 222 | for (int i = 0, ofs = 0; i < argvArray.Length; i++, ofs += IntPtr.Size) 223 | argvArray[i] = (nuint)(nint)Marshal.ReadIntPtr(argv, ofs); 224 | } 225 | else 226 | argvArray = Array.Empty(); 227 | 228 | object userdataObj = userdata != IntPtr.Zero ? GCHandle.FromIntPtr(userdata) : null; 229 | return cbFunction(argvArray, userdataObj); 230 | } 231 | catch (Exception ex) 232 | { 233 | PluginMain.LogUnhandledException(ex); 234 | return default; 235 | } 236 | }; 237 | 238 | return _plugin_registerexprfunction_core(pluginHandle, name, argc, callback, userdata); 239 | } 240 | 241 | public static bool _plugin_registerexprfunction(int pluginHandle, string name, int argc, CBPLUGINEXPRFUNCTION_RAWARGS cbFunction, object userdata) 242 | { 243 | if (name == null) 244 | throw new ArgumentNullException(nameof(name)); 245 | 246 | if (cbFunction == null) 247 | throw new ArgumentNullException(nameof(cbFunction)); 248 | 249 | CBPLUGINEXPRFUNCTION_NATIVE callback = (argc, argv, userdata) => 250 | { 251 | try 252 | { 253 | object userdataObj = userdata != IntPtr.Zero ? GCHandle.FromIntPtr(userdata) : null; 254 | return cbFunction(argc, argv, userdataObj); 255 | } 256 | catch (Exception ex) 257 | { 258 | PluginMain.LogUnhandledException(ex); 259 | return default; 260 | } 261 | }; 262 | 263 | return _plugin_registerexprfunction_core(pluginHandle, name, argc, callback, userdata); 264 | } 265 | 266 | [DllImport(dll, CallingConvention = cdecl, EntryPoint = nameof(_plugin_unregisterexprfunction), ExactSpelling = true)] 267 | private static extern bool _plugin_unregisterexprfunction_native(int pluginHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string name); 268 | 269 | public static bool _plugin_unregisterexprfunction(int pluginHandle, string name) 270 | { 271 | if (name == null) 272 | throw new ArgumentNullException(nameof(name)); 273 | 274 | lock (_expressionFunctionCallbacks) 275 | { 276 | var success = _plugin_unregisterexprfunction_native(pluginHandle, name); 277 | 278 | if (success) 279 | { 280 | if (_expressionFunctionCallbacks.TryGetValue(name, out var callbackInfo)) 281 | { 282 | if (callbackInfo.Item2.IsAllocated) 283 | callbackInfo.Item2.Free(); 284 | 285 | _expressionFunctionCallbacks.Remove(name); 286 | } 287 | } 288 | 289 | return success; 290 | } 291 | } 292 | 293 | #endregion 294 | 295 | #region Menus 296 | 297 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 298 | public static extern int _plugin_menuadd(int hMenu, [MarshalAs(UnmanagedType.LPUTF8Str)] string title); 299 | 300 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 301 | public static extern bool _plugin_menuaddentry(int hMenu, int hEntry, [MarshalAs(UnmanagedType.LPUTF8Str)] string title); 302 | 303 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 304 | public static extern bool _plugin_menuaddseparator(int hMenu); 305 | 306 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 307 | public static extern void _plugin_menuseticon(int hMenu, ref BridgeBase.ICONDATA icon); 308 | 309 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 310 | public static extern void _plugin_menuentryseticon(int pluginHandle, int hEntry, ref BridgeBase.ICONDATA icon); 311 | 312 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 313 | public static extern void _plugin_menuentrysetchecked(int pluginHandle, int hEntry, bool @checked); 314 | 315 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 316 | public static extern void _plugin_menusetvisible(int pluginHandle, int hMenu, bool visible); 317 | 318 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 319 | public static extern void _plugin_menuentrysetvisible(int pluginHandle, int hEntry, bool visible); 320 | 321 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 322 | public static extern void _plugin_menusetname(int pluginHandle, int hMenu, [MarshalAs(UnmanagedType.LPUTF8Str)] string name); 323 | 324 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 325 | public static extern void _plugin_menuentrysetname(int pluginHandle, int hEntry, [MarshalAs(UnmanagedType.LPUTF8Str)] string name); 326 | 327 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 328 | public static extern void _plugin_menuentrysethotkey(int pluginHandle, int hEntry, [MarshalAs(UnmanagedType.LPUTF8Str)] string hotkey); 329 | 330 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 331 | public static extern bool _plugin_menuremove(int hMenu); 332 | 333 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 334 | public static extern bool _plugin_menuentryremove(int pluginHandle, int hEntry); 335 | 336 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 337 | public static extern bool _plugin_menuclear(int hMenu); 338 | 339 | #endregion 340 | 341 | [DllImport(dll, CallingConvention = cdecl, ExactSpelling = true)] 342 | public static extern void _plugin_debugskipexceptions(bool skip); 343 | 344 | #pragma warning disable 0649 345 | 346 | [Serializable] 347 | public unsafe struct PLUG_INITSTRUCT 348 | { 349 | public int pluginHandle; 350 | public int sdkVersion; 351 | public int pluginVersion; 352 | 353 | private const int pluginNameSize = 256; 354 | public fixed byte pluginNameBytes[pluginNameSize]; 355 | public string pluginName 356 | { 357 | get 358 | { 359 | fixed (byte* ptr = pluginNameBytes) 360 | return new IntPtr(ptr).MarshalToStringUTF8(pluginNameSize); 361 | } 362 | set 363 | { 364 | fixed (byte* ptr = pluginNameBytes) 365 | value.MarshalToPtrUTF8(new IntPtr(ptr), pluginNameSize); 366 | } 367 | } 368 | } 369 | 370 | [Serializable] 371 | public struct PLUG_SETUPSTRUCT 372 | { 373 | public IntPtr hwndDlg; //gui window handle 374 | public int hMenu; //plugin menu handle 375 | public int hMenuDisasm; //plugin disasm menu handle 376 | public int hMenuDump; //plugin dump menu handle 377 | public int hMenuStack; //plugin stack menu handle 378 | public int hMenuGraph; //plugin graph menu handle 379 | public int hMenuMemmap; //plugin memory map menu handle 380 | public int hMenuSymmod; //plugin symbol module menu handle 381 | } 382 | 383 | public enum CBTYPE 384 | { 385 | CB_INITDEBUG, //PLUG_CB_INITDEBUG 386 | CB_STOPDEBUG, //PLUG_CB_STOPDEBUG 387 | CB_CREATEPROCESS, //PLUG_CB_CREATEPROCESS 388 | CB_EXITPROCESS, //PLUG_CB_EXITPROCESS 389 | CB_CREATETHREAD, //PLUG_CB_CREATETHREAD 390 | CB_EXITTHREAD, //PLUG_CB_EXITTHREAD 391 | CB_SYSTEMBREAKPOINT, //PLUG_CB_SYSTEMBREAKPOINT 392 | CB_LOADDLL, //PLUG_CB_LOADDLL 393 | CB_UNLOADDLL, //PLUG_CB_UNLOADDLL 394 | CB_OUTPUTDEBUGSTRING, //PLUG_CB_OUTPUTDEBUGSTRING 395 | CB_EXCEPTION, //PLUG_CB_EXCEPTION 396 | CB_BREAKPOINT, //PLUG_CB_BREAKPOINT 397 | CB_PAUSEDEBUG, //PLUG_CB_PAUSEDEBUG 398 | CB_RESUMEDEBUG, //PLUG_CB_RESUMEDEBUG 399 | CB_STEPPED, //PLUG_CB_STEPPED 400 | CB_ATTACH, //PLUG_CB_ATTACHED (before attaching, after CB_INITDEBUG) 401 | CB_DETACH, //PLUG_CB_DETACH (before detaching, before CB_STOPDEBUG) 402 | CB_DEBUGEVENT, //PLUG_CB_DEBUGEVENT (called on any debug event) 403 | CB_MENUENTRY, //PLUG_CB_MENUENTRY 404 | CB_WINEVENT, //PLUG_CB_WINEVENT 405 | CB_WINEVENTGLOBAL, //PLUG_CB_WINEVENTGLOBAL 406 | CB_LOADDB, //PLUG_CB_LOADSAVEDB 407 | CB_SAVEDB, //PLUG_CB_LOADSAVEDB 408 | CB_FILTERSYMBOL, //PLUG_CB_FILTERSYMBOL 409 | CB_TRACEEXECUTE, //PLUG_CB_TRACEEXECUTE 410 | CB_SELCHANGED, //PLUG_CB_SELCHANGED 411 | CB_ANALYZE, //PLUG_CB_ANALYZE 412 | CB_ADDRINFO, //PLUG_CB_ADDRINFO 413 | CB_VALFROMSTRING, //PLUG_CB_VALFROMSTRING 414 | CB_VALTOSTRING, //PLUG_CB_VALTOSTRING 415 | CB_MENUPREPARE, //PLUG_CB_MENUPREPARE 416 | CB_STOPPINGDEBUG, //PLUG_CB_STOPDEBUG 417 | CB_LAST 418 | } 419 | 420 | [Serializable] 421 | public struct PLUG_CB_INITDEBUG 422 | { 423 | public Utf8StringRef szFileName; 424 | } 425 | 426 | [Serializable] 427 | public struct PLUG_CB_CREATEPROCESS 428 | { 429 | private IntPtr CreateProcessInfoPtr; 430 | public StructRef CreateProcessInfo => new StructRef(CreateProcessInfoPtr); 431 | 432 | private IntPtr modInfoPtr; 433 | public IMAGEHLP_MODULE64? modInfo => modInfoPtr.ToStruct(); 434 | 435 | public Utf8StringRef DebugFileName; 436 | 437 | private IntPtr fdProcessInfoPtr; //WAPI.PROCESS_INFORMATION 438 | public StructRef fdProcessInfo => new StructRef(fdProcessInfoPtr); 439 | } 440 | 441 | [Serializable] 442 | public struct PLUG_CB_EXITPROCESS 443 | { 444 | private IntPtr ExitProcessPtr; 445 | public StructRef ExitProcess => new StructRef(ExitProcessPtr); 446 | } 447 | 448 | [Serializable] 449 | public struct PLUG_CB_CREATETHREAD 450 | { 451 | private IntPtr CreateThreadPtr; 452 | public StructRef CreateThread => new StructRef(CreateThreadPtr); 453 | 454 | public uint dwThreadId; 455 | } 456 | 457 | [Serializable] 458 | public struct PLUG_CB_EXITTHREAD 459 | { 460 | private IntPtr ExitThreadPtr; 461 | public StructRef ExitThread => new StructRef(ExitThreadPtr); 462 | 463 | public uint dwThreadId; 464 | } 465 | 466 | [Serializable] 467 | public struct PLUG_CB_SYSTEMBREAKPOINT 468 | { 469 | public IntPtr reserved; 470 | } 471 | 472 | [Serializable] 473 | public struct PLUG_CB_LOADDLL 474 | { 475 | private IntPtr LoadDllPtr; 476 | public StructRef LoadDll => new StructRef(LoadDllPtr); 477 | 478 | private IntPtr modInfoPtr; 479 | public IMAGEHLP_MODULE64? modInfo => modInfoPtr.ToStruct(); 480 | 481 | public Utf8StringRef modname; 482 | } 483 | 484 | [Serializable] 485 | public struct PLUG_CB_UNLOADDLL 486 | { 487 | private IntPtr UnloadDllPtr; 488 | public StructRef UnloadDll => new StructRef(UnloadDllPtr); 489 | } 490 | 491 | [Serializable] 492 | public struct PLUG_CB_OUTPUTDEBUGSTRING 493 | { 494 | private IntPtr DebugStringPtr; 495 | public StructRef DebugString => new StructRef(DebugStringPtr); 496 | } 497 | 498 | [Serializable] 499 | public struct PLUG_CB_EXCEPTION 500 | { 501 | private IntPtr ExceptionPtr; 502 | public StructRef Exception => new StructRef(ExceptionPtr); 503 | } 504 | 505 | [Serializable] 506 | public struct PLUG_CB_BREAKPOINT 507 | { 508 | // TODO: add definition for struct BRIDGEBP 509 | public IntPtr breakpoint; 510 | } 511 | 512 | [Serializable] 513 | public struct PLUG_CB_PAUSEDEBUG 514 | { 515 | public IntPtr reserved; 516 | } 517 | 518 | [Serializable] 519 | public struct PLUG_CB_RESUMEDEBUG 520 | { 521 | public IntPtr reserved; 522 | } 523 | 524 | [Serializable] 525 | public struct PLUG_CB_STEPPED 526 | { 527 | public IntPtr reserved; 528 | } 529 | 530 | [Serializable] 531 | public struct PLUG_CB_ATTACH 532 | { 533 | public uint dwProcessId; 534 | } 535 | 536 | [Serializable] 537 | public struct PLUG_CB_DETACH 538 | { 539 | private IntPtr fdProcessInfoPtr; 540 | public StructRef fdProcessInfo => new StructRef(fdProcessInfoPtr); 541 | } 542 | 543 | [Serializable] 544 | public struct PLUG_CB_DEBUGEVENT 545 | { 546 | private IntPtr DebugEventPtr; 547 | public StructRef DebugEvent => new StructRef(DebugEventPtr); 548 | } 549 | 550 | [Serializable] 551 | public struct PLUG_CB_MENUENTRY 552 | { 553 | public int hEntry; 554 | } 555 | 556 | [Serializable] 557 | public struct PLUG_CB_WINEVENT 558 | { 559 | private IntPtr messagePtr; 560 | public StructRef message => new StructRef(messagePtr); 561 | 562 | public IntPtr result; 563 | public BlittableBoolean retval; 564 | } 565 | 566 | [Serializable] 567 | public struct PLUG_CB_WINEVENTGLOBAL 568 | { 569 | private IntPtr messagePtr; 570 | public StructRef message => new StructRef(messagePtr); 571 | 572 | public BlittableBoolean retval; 573 | } 574 | 575 | [Serializable] 576 | public struct PLUG_CB_LOADSAVEDB 577 | { 578 | // TODO: add definition for struct json_t 579 | public IntPtr root; 580 | public int loadSaveType; 581 | } 582 | 583 | [Serializable] 584 | public struct PLUG_CB_FILTERSYMBOL 585 | { 586 | public Utf8StringRef symbol; 587 | public BlittableBoolean retval; 588 | } 589 | 590 | [Serializable] 591 | public struct PLUG_CB_TRACEEXECUTE 592 | { 593 | public nuint cip; 594 | public BlittableBoolean stop; 595 | } 596 | 597 | [Serializable] 598 | public struct PLUG_CB_SELCHANGED 599 | { 600 | public int hWindow; 601 | public nuint VA; 602 | } 603 | 604 | [Serializable] 605 | public struct PLUG_CB_ANALYZE 606 | { 607 | public BridgeBase.BridgeCFGraphList graph; 608 | } 609 | 610 | [Serializable] 611 | public struct PLUG_CB_ADDRINFO 612 | { 613 | public nuint addr; 614 | 615 | private IntPtr addrinfoPtr; 616 | public StructRef addrinfo => new StructRef(addrinfoPtr); 617 | 618 | public BlittableBoolean retval; 619 | } 620 | 621 | [Serializable] 622 | public struct PLUG_CB_VALFROMSTRING 623 | { 624 | public Utf8StringRef @string; 625 | public nuint value; 626 | 627 | private IntPtr value_sizePtr; 628 | public StructRef value_size => new StructRef(value_sizePtr); 629 | 630 | private IntPtr isvarPtr; 631 | public StructRef isvar => new StructRef(isvarPtr); 632 | 633 | private IntPtr hexonlyPtr; 634 | public StructRef hexonly => new StructRef(hexonlyPtr); 635 | 636 | public BlittableBoolean retval; 637 | } 638 | 639 | [Serializable] 640 | public struct PLUG_CB_VALTOSTRING 641 | { 642 | public Utf8StringRef @string; 643 | public nuint value; 644 | public BlittableBoolean retval; 645 | } 646 | 647 | [Serializable] 648 | public struct PLUG_CB_MENUPREPARE 649 | { 650 | public BridgeBase.GUIMENUTYPE hMenu; 651 | } 652 | 653 | [Serializable] 654 | public struct PLUG_CB_STOPDEBUG 655 | { 656 | public IntPtr reserved; 657 | } 658 | } 659 | } 660 | -------------------------------------------------------------------------------- /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.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.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.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.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/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/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 | -------------------------------------------------------------------------------- /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 | 9 | namespace DotNetPlugin 10 | { 11 | /// 12 | /// Contains entry points for plugin lifecycle and debugger event callbacks. 13 | /// 14 | internal static class PluginMain 15 | { 16 | #if ALLOW_UNLOADING 17 | private static readonly Lazy NullSession = new Lazy(() => PluginSession.Null, LazyThreadSafetyMode.PublicationOnly); 18 | private static volatile Lazy s_session = NullSession; 19 | private static IPluginSession Session => s_session.Value; 20 | 21 | private static readonly string s_controlCommand = PluginBase.PluginName.Replace(' ', '_'); 22 | 23 | internal static readonly string ImplAssemblyLocation; 24 | #else 25 | private static PluginSession Session = PluginSession.Null; 26 | #endif 27 | 28 | private static int s_pluginHandle; 29 | private static Plugins.PLUG_SETUPSTRUCT s_setupStruct; 30 | 31 | private static Assembly TryLoadAssemblyFrom(AssemblyName assemblyName, string location, bool tryLoadFromMemory = false) 32 | { 33 | var pluginBasePath = Path.GetDirectoryName(location); 34 | var dllPath = Path.Combine(pluginBasePath, assemblyName.Name + ".dll"); 35 | 36 | if (!File.Exists(dllPath)) 37 | return null; 38 | 39 | if (tryLoadFromMemory) 40 | { 41 | var assemblyBytes = File.ReadAllBytes(dllPath); 42 | // first we try to load the assembly from memory so that it doesn't get locked 43 | try { return Assembly.Load(assemblyBytes); } 44 | // mixed-mode assemblies can't be loaded from memory, so we resort to loading it from the disk 45 | catch { } 46 | } 47 | 48 | return Assembly.LoadFile(dllPath); 49 | } 50 | 51 | static PluginMain() 52 | { 53 | if (AppDomain.CurrentDomain.IsDefaultAppDomain()) 54 | { 55 | AppDomain.CurrentDomain.UnhandledException += (s, e) => LogUnhandledException(e.ExceptionObject); 56 | 57 | // by default the runtime will look for referenced assemblies in the directory of the host application, 58 | // not in the plugin's dictionary, so we need to customize assembly resolving to fix this 59 | AppDomain.CurrentDomain.AssemblyResolve += (s, e) => 60 | { 61 | var assemblyName = new AssemblyName(e.Name); 62 | 63 | if (assemblyName.Name == typeof(PluginMain).Assembly.GetName().Name) 64 | return typeof(PluginMain).Assembly; 65 | 66 | return TryLoadAssemblyFrom(assemblyName, typeof(PluginMain).Assembly.Location); 67 | }; 68 | } 69 | #if ALLOW_UNLOADING 70 | else 71 | { 72 | AppDomain.CurrentDomain.AssemblyResolve += (s, e) => 73 | { 74 | var assemblyName = new AssemblyName(e.Name); 75 | 76 | if (assemblyName.Name == typeof(PluginMain).Assembly.GetName().Name) 77 | return typeof(PluginMain).Assembly; 78 | 79 | return 80 | (ImplAssemblyLocation != null ? TryLoadAssemblyFrom(assemblyName, ImplAssemblyLocation, tryLoadFromMemory: true) : null) ?? 81 | TryLoadAssemblyFrom(assemblyName, typeof(PluginMain).Assembly.Location, tryLoadFromMemory: true); 82 | }; 83 | } 84 | 85 | using (var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("build.meta")) 86 | { 87 | if (resourceStream == null) 88 | return; 89 | 90 | ImplAssemblyLocation = new StreamReader(resourceStream).ReadLine(); 91 | } 92 | #endif 93 | } 94 | 95 | public static void LogUnhandledException(object exceptionObject) 96 | { 97 | var location = typeof(PluginMain).Assembly.Location; 98 | var logPath = Path.ChangeExtension(location, ".log"); 99 | 100 | var errorMessage = exceptionObject?.ToString(); 101 | if (errorMessage != null) 102 | { 103 | errorMessage += Environment.NewLine; 104 | File.AppendAllText(logPath, errorMessage); 105 | PluginBase.LogError(errorMessage); 106 | } 107 | } 108 | 109 | #if ALLOW_UNLOADING 110 | private static void HandleImplChanged(object sender) 111 | { 112 | var session = s_session; 113 | if (ReferenceEquals(session.Value, sender) && UnloadPlugin(session)) 114 | LoadPlugin(session); 115 | } 116 | 117 | private static bool LoadPlugin(Lazy reloadedSession = null) 118 | { 119 | if (!TryLoadPlugin(isInitial: false, reloadedSession)) 120 | { 121 | PluginBase.LogError("Failed to load the implementation assembly."); 122 | return false; 123 | } 124 | 125 | Session.PluginHandle = s_pluginHandle; 126 | 127 | if (!Session.Init()) 128 | { 129 | PluginBase.LogError("Failed to initialize the implementation assembly."); 130 | TryUnloadPlugin(); 131 | return false; 132 | } 133 | 134 | Session.Setup(ref s_setupStruct); 135 | 136 | PluginBase.LogInfo("Successfully loaded the implementation assembly."); 137 | return true; 138 | } 139 | 140 | private static bool UnloadPlugin(Lazy reloadedSession = null) 141 | { 142 | if (!TryUnloadPlugin(reloadedSession)) 143 | { 144 | PluginBase.LogError("Failed to unload the implementation assembly."); 145 | return false; 146 | } 147 | 148 | PluginBase.LogInfo("Successfully unloaded the implementation assembly."); 149 | return true; 150 | } 151 | 152 | private static bool TryLoadPlugin(bool isInitial, Lazy reloadedSession = null) 153 | { 154 | var expectedSession = reloadedSession ?? NullSession; 155 | var newSession = new Lazy(() => new PluginSessionProxy(HandleImplChanged), LazyThreadSafetyMode.ExecutionAndPublication); 156 | var originalSession = Interlocked.CompareExchange(ref s_session, newSession, expectedSession); 157 | if (originalSession == expectedSession) 158 | { 159 | _ = newSession.Value; // forces creation of session 160 | 161 | return true; 162 | } 163 | 164 | return false; 165 | } 166 | 167 | private static bool TryUnloadPlugin(Lazy reloadedSession = null) 168 | { 169 | Lazy originalSession; 170 | 171 | if (reloadedSession == null) 172 | { 173 | originalSession = Interlocked.Exchange(ref s_session, NullSession); 174 | } 175 | else 176 | { 177 | originalSession = 178 | Interlocked.CompareExchange(ref s_session, reloadedSession, reloadedSession) == reloadedSession ? 179 | reloadedSession : 180 | NullSession; 181 | } 182 | 183 | if (originalSession != NullSession) 184 | { 185 | originalSession.Value.Dispose(); 186 | return true; 187 | } 188 | 189 | return false; 190 | } 191 | 192 | #else 193 | private static bool TryLoadPlugin(bool isInitial) 194 | { 195 | if (isInitial) 196 | { 197 | Session = new PluginSession(); 198 | return true; 199 | } 200 | 201 | return false; 202 | } 203 | 204 | private static bool TryUnloadPlugin() 205 | { 206 | return false; 207 | } 208 | #endif 209 | 210 | [DllExport("pluginit", CallingConvention.Cdecl)] 211 | public static bool pluginit(ref Plugins.PLUG_INITSTRUCT initStruct) 212 | { 213 | if (!TryLoadPlugin(isInitial: true)) 214 | return false; 215 | 216 | initStruct.sdkVersion = Plugins.PLUG_SDKVERSION; 217 | initStruct.pluginVersion = PluginBase.PluginVersion; 218 | initStruct.pluginName = PluginBase.PluginName; 219 | Session.PluginHandle = s_pluginHandle = initStruct.pluginHandle; 220 | 221 | #if ALLOW_UNLOADING 222 | if (!Plugins._plugin_registercommand(s_pluginHandle, s_controlCommand, ControlCommand, false)) 223 | { 224 | PluginBase.LogError($"Failed to register the \"'{s_controlCommand}'\" command."); 225 | TryUnloadPlugin(); 226 | return false; 227 | } 228 | #endif 229 | 230 | if (!Session.Init()) 231 | { 232 | PluginBase.LogError("Failed to initialize the implementation assembly."); 233 | TryUnloadPlugin(); 234 | return false; 235 | } 236 | 237 | return true; 238 | } 239 | 240 | [DllExport("plugsetup", CallingConvention.Cdecl)] 241 | private static void plugsetup(ref Plugins.PLUG_SETUPSTRUCT setupStruct) 242 | { 243 | s_setupStruct = setupStruct; 244 | 245 | Session.Setup(ref setupStruct); 246 | } 247 | 248 | [DllExport("plugstop", CallingConvention.Cdecl)] 249 | private static bool plugstop() 250 | { 251 | var success = Session.Stop(); 252 | 253 | #if ALLOW_UNLOADING 254 | Plugins._plugin_unregistercommand(s_pluginHandle, s_controlCommand); 255 | #endif 256 | 257 | s_setupStruct = default; 258 | s_pluginHandle = default; 259 | 260 | return success; 261 | } 262 | 263 | #if ALLOW_UNLOADING 264 | private static bool ControlCommand(string[] args) 265 | { 266 | if (args.Length > 1) 267 | { 268 | if ("load".Equals(args[1], StringComparison.OrdinalIgnoreCase)) 269 | { 270 | return LoadPlugin(); 271 | } 272 | else if ("unload".Equals(args[1], StringComparison.OrdinalIgnoreCase)) 273 | { 274 | return UnloadPlugin(); 275 | } 276 | } 277 | 278 | PluginBase.LogError($"Invalid syntax. Usage: {s_controlCommand} [load|unload]"); 279 | return false; 280 | } 281 | #endif 282 | 283 | [DllExport("CBMENUENTRY", CallingConvention.Cdecl)] 284 | public static void CBMENUENTRY(Plugins.CBTYPE cbType, ref Plugins.PLUG_CB_MENUENTRY info) 285 | { 286 | Session.OnMenuEntry(ref info); 287 | } 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /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/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 -------------------------------------------------------------------------------- /DotNetPluginCS.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 16 3 | VisualStudioVersion = 16.0.31829.152 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|x64 = Debug|x64 19 | Debug|x86 = Debug|x86 20 | Release|x64 = Release|x64 21 | Release|x86 = Release|x86 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Debug|x64.ActiveCfg = Debug|x64 25 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Debug|x64.Build.0 = Debug|x64 26 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Debug|x86.ActiveCfg = Debug|x86 27 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Debug|x86.Build.0 = Debug|x86 28 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Release|x64.ActiveCfg = Release|x64 29 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Release|x64.Build.0 = Release|x64 30 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Release|x86.ActiveCfg = Release|x86 31 | {F2F8BA6A-0112-47CA-9AFA-E8082263AB72}.Release|x86.Build.0 = Release|x86 32 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Debug|x64.ActiveCfg = Debug|x64 33 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Debug|x64.Build.0 = Debug|x64 34 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Debug|x86.ActiveCfg = Debug|x86 35 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Debug|x86.Build.0 = Debug|x86 36 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Release|x64.ActiveCfg = Release|x64 37 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Release|x64.Build.0 = Release|x64 38 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Release|x86.ActiveCfg = Release|x86 39 | {3F05E028-6436-4F1C-ABAF-5E7CFBFCB7D7}.Release|x86.Build.0 = Release|x86 40 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Debug|x64.ActiveCfg = Debug|x64 41 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Debug|x64.Build.0 = Debug|x64 42 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Debug|x86.ActiveCfg = Debug|x86 43 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Debug|x86.Build.0 = Debug|x86 44 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Release|x64.ActiveCfg = Release|x64 45 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Release|x64.Build.0 = Release|x64 46 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Release|x86.ActiveCfg = Release|x86 47 | {CF3CB686-DD3D-4782-9192-8195A968DC09}.Release|x86.Build.0 = Release|x86 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {04FC09BA-059D-44FA-A886-0B8E3C34B00F} 54 | EndGlobalSection 55 | EndGlobal 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DotNetPluginCS - A .NET Framework plugin template for x86dbg 2 | This project provides a foundation for developing plugins for [x64dbg](https://x64dbg.com/) in C#, on the .NET platform. (.NET means the "classic", Windows-only .NET Framework in this case.) 3 | 4 | On top of defining essential native bindings which are necessary to interact with the debugger host, it also provides a straightforward project structure and an ergonomic wrapper API over the aforementioned low-level bindings. 5 | 6 | The template is architected so that it can speed up test-change-test development cycles greatly. At development time, whenever you need to make a change during testing, you'll only have to rebuild your plugin and it will be reloaded automatically into the host. 7 | 8 | ## Prerequisites 9 | To build the project, you'll minimally need 10 | * [Visual Studio Build Tools](https://docs.microsoft.com/hu-hu/visualstudio/releases/2019/history) (2019 v16.7 or later) 11 | * [.NET Core 3.1/.NET 6+ SDK](https://dotnet.microsoft.com/en-us/download/dotnet) (required only for building) 12 | * [.NET Framework 4.7.2 SDK](https://dotnet.microsoft.com/en-us/download/dotnet-framework/net472) 13 | 14 | However, probably you'd just have an easier time with the full [Visual Studio IDE](https://visualstudio.microsoft.com/) (2019 v16.7 or later, Community edition is sufficient) since that includes all of the required components. Other IDEs which support C# 9 might also do but it's not tested. 15 | 16 | ## Getting Started 17 | 18 | 1. Fork the project and create a local copy (`git clone `). 19 | 2. Open *Directory.Build.props* and change the `PluginName` and `PluginAssemblyName` properties to your liking. 20 | 3. The actual implementation of the plugin logic resides in the _DotNetPlugin.Impl_ project. (Usually you won’t need to touch the other ones.) So, open *DotNetPlugin.Impl/Plugin.cs*, which is the entry point to your code. You find some further information there. 21 | 5. You can quickly start to implement your ideas by examining the samples to be found in the following files: 22 | * *Plugin.Commands.cs* - Here you can define custom [commands](https://help.x64dbg.com/en/latest/introduction/Input.html#commands) using the `Command` attribute. Methods marked with this attribute will be automatically discovered (using reflection) and registered. (Both static and instance methods are allowed as handlers, they just need to have the right signature. Return type can be `void`, in which case the command will always report success.) 23 | * *Plugin.EventCallbacks.cs* - Here you can register [callbacks](https://help.x64dbg.com/en/latest/developers/plugins/basics.html#exports) to get notified of debugger events. Use the `EventCallback` attribute to make them automatically registered, just like in the case of commands. Further remarks: pay close attention to the method signature. The parameter type must match the event type specified in the attribute, otherwise it won't work. You can look up this mapping e.g. in [the plugin SDK definition](https://github.com/x64dbg/x64dbg/blob/29bb559aa6ac5155ff518b43f3c84f4a72abd8bf/src/dbg/_plugins.h#L260). Also keep in mind [this warning from the docs](https://help.x64dbg.com/en/latest/developers/plugins/Callbacks/index.html): 24 | > In general AVOID time-consuming operations inside callbacks, do these in separate threads. 25 | * *Plugin.ExpressionFunction.cs* - Here you can define [expression functions](https://help.x64dbg.com/en/latest/introduction/Expression-functions.html) using the `ExpressionFunction` attribute. Works in the same way as commands. The return type, however, must always be `nuint` (`UIntPtr`). 26 | * *Plugin.Menus.cs* - In the `SetupMenu` method you can register menu items and sub-menus via a fluent-like API to extend the various menus of the host. 27 | 28 | ## Development 29 | After implementing something useful, you can test your plugin like this: 30 | 1. Build the `DotNetPlugin.Impl` project (or the whole solution - the end result should be the same) **in *Debug* configuration**. 31 | 2. In the root directory of the project a folder named *bin* will be created. Locate the *.dp32*/*.dp64* file (depending on the target CPU architecture of the build). Copy that file together with *DotNetPlugin.RemotingHelper.dll* to the corresponding *plugins* directory of x64dbg. 32 | 3. Run the debugger. 33 | 4. Test your plugin. 34 | 5. Modify the plugin if needed and rebuild it. At this point it will be automatically reloaded, so goto 4 until you're satisfied. 35 | 36 | ## Release 37 | When you decide that the plugin is ready for use, you can create a performance-optimized version of it. So, change the **build configuration to *Release*** and build it. Now you'll only need the single *.dp32*/*.dp64* file. 38 | 39 | ## Samples 40 | * [tracecalls](https://github.com/adams85/DotNetPluginCS/tree/tracecalls) by adams85 - An attempt on implementing WinDbg's `wt` command (or at least something similar to that) for x64dbg. 41 | 42 | ## Project Status 43 | The wiring-up to the host is pretty much complete now and it's wrapped in a more ergonomic API (though the low-level APIs are also available if needed). 44 | 45 | However, many functions exposed by the host (especially, C++ APIs) are still missing. Currently there's no plan to add bindings for these to make them available out of the box. Plugin writers can add them manually as needed. Pushing such changes back upstream would be greatly appreciated. 46 | 47 | As an alternative, you can use the more complete bindings of the [DotX64Dbg](https://github.com/x64dbg/DotX64Dbg), another .NET plugin project. This one runs on .NET Core and uses a different approach but the bindings defined there (using C++/CLI) can be used with this solution too. A proof of concept for this can be found [here](https://github.com/adams85/DotNetPluginCS/tree/dotx64dbg-backport). 48 | 49 | ### Happy coding & debugging! 50 | --------------------------------------------------------------------------------