├── .gitignore ├── LICENSE ├── README.md ├── TinyJitHook.Core.TestApp ├── Program.cs └── TinyJitHook.Core.TestApp.csproj ├── TinyJitHook.Core ├── Directory.Build.targets ├── ExceptionHandler.cs ├── Extensions │ ├── ExceptionHandlerByteHelper.cs │ ├── InstructionByteHelper.cs │ ├── JitHelper.cs │ ├── OpCodeShortHelper.cs │ ├── PathHelper.cs │ └── ReflectionHelper.cs ├── HookHelpers │ ├── HookHelper32.cs │ └── HookHelper64.cs ├── Instruction.cs ├── Logger.cs ├── MainJitHook.cs ├── Models │ └── IHookHelper.cs ├── Program.cs ├── SJITHook │ ├── ClrjitAddrProvider.cs │ ├── Data.cs │ ├── IJitHook.cs │ ├── JITHook.cs │ ├── JITHook64.cs │ └── VTableAddrProvider.cs └── TinyJitHook.Core.csproj ├── TinyJitHook.sln └── TinyJitHook ├── App.config ├── ExceptionHandler.cs ├── Extensions ├── ExceptionHandlerByteHelper.cs ├── InstructionByteHelper.cs ├── JitHelper.cs ├── OpCodeShortHelper.cs ├── PathHelper.cs └── ReflectionHelper.cs ├── HookHelpers ├── HookHelper32.cs └── HookHelper64.cs ├── InjectionTest.cs ├── Instruction.cs ├── Logger.cs ├── MainJitHook.cs ├── Models └── IHookHelper.cs ├── Program.cs ├── Properties └── AssemblyInfo.cs ├── SJITHook ├── ClrjitAddrProvider.cs ├── Data.cs ├── IJitHook.cs ├── JITHook.cs ├── JITHook64.cs ├── MscorjitAddrProvider.cs └── VTableAddrProvider.cs └── TinyJitHook.csproj /.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 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugCore/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleaseCore/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | bld/ 25 | [Bb]in/ 26 | [Oo]bj/ 27 | [Ll]og/ 28 | 29 | # Visual Studio 2015 cache/options directory 30 | .vs/ 31 | # Uncomment if you have tasks that create the project's static files in wwwroot 32 | #wwwroot/ 33 | 34 | # MSTest test Results 35 | [Tt]est[Rr]esult*/ 36 | [Bb]uild[Ll]og.* 37 | 38 | # NUNIT 39 | *.VisualState.xml 40 | TestResult.xml 41 | 42 | # Build Results of an ATL Project 43 | [Dd]ebugPS/ 44 | [Rr]eleasePS/ 45 | dlldata.c 46 | 47 | # .NET Core 48 | project.lock.json 49 | project.fragment.lock.json 50 | artifacts/ 51 | **/Properties/launchSettings.json 52 | 53 | *_i.c 54 | *_p.c 55 | *_i.h 56 | *.ilk 57 | *.meta 58 | *.obj 59 | *.pch 60 | *.pdb 61 | *.pgc 62 | *.pgd 63 | *.rsp 64 | *.sbr 65 | *.tlb 66 | *.tli 67 | *.tlh 68 | *.tmp 69 | *.tmp_proj 70 | *.log 71 | *.vspscc 72 | *.vssscc 73 | .builds 74 | *.pidb 75 | *.svclog 76 | *.scc 77 | 78 | # Chutzpah Test files 79 | _Chutzpah* 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opendb 86 | *.opensdf 87 | *.sdf 88 | *.cachefile 89 | *.VC.db 90 | *.VC.VC.opendb 91 | 92 | # Visual Studio profiler 93 | *.psess 94 | *.vsp 95 | *.vspx 96 | *.sap 97 | 98 | # TFS 2012 Local Workspace 99 | $tf/ 100 | 101 | # Guidance Automation Toolkit 102 | *.gpState 103 | 104 | # ReSharper is a .NET coding add-in 105 | _ReSharper*/ 106 | *.[Rr]e[Ss]harper 107 | *.DotSettings.user 108 | 109 | # JustCode is a .NET coding add-in 110 | .JustCode 111 | 112 | # TeamCity is a build add-in 113 | _TeamCity* 114 | 115 | # DotCover is a Code Coverage Tool 116 | *.dotCover 117 | 118 | # Visual Studio code coverage results 119 | *.coverage 120 | *.coveragexml 121 | 122 | # NCrunch 123 | _NCrunch_* 124 | .*crunch*.local.xml 125 | nCrunchTemp_* 126 | 127 | # MightyMoose 128 | *.mm.* 129 | AutoTest.Net/ 130 | 131 | # Web workbench (sass) 132 | .sass-cache/ 133 | 134 | # Installshield output folder 135 | [Ee]xpress/ 136 | 137 | # DocProject is a documentation generator add-in 138 | DocProject/buildhelp/ 139 | DocProject/Help/*.HxT 140 | DocProject/Help/*.HxC 141 | DocProject/Help/*.hhc 142 | DocProject/Help/*.hhk 143 | DocProject/Help/*.hhp 144 | DocProject/Help/Html2 145 | DocProject/Help/html 146 | 147 | # Click-Once directory 148 | publish/ 149 | 150 | # Publish Web Output 151 | *.[Pp]ublish.xml 152 | *.azurePubxml 153 | # TODO: Comment the next line if you want to checkin your web deploy settings 154 | # but database connection strings (with potential passwords) will be unencrypted 155 | *.pubxml 156 | *.publishproj 157 | 158 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 159 | # checkin your Azure Web App publish settings, but sensitive information contained 160 | # in these scripts will be unencrypted 161 | PublishScripts/ 162 | 163 | # NuGet Packages 164 | *.nupkg 165 | # The packages folder can be ignored because of Package Restore 166 | **/packages/* 167 | # except build/, which is used as an MSBuild target. 168 | !**/packages/build/ 169 | # Uncomment if necessary however generally it will be regenerated when needed 170 | #!**/packages/repositories.config 171 | # NuGet v3's project.json files produces more ignorable files 172 | *.nuget.props 173 | *.nuget.targets 174 | 175 | # Microsoft Azure Build Output 176 | csx/ 177 | *.build.csdef 178 | 179 | # Microsoft Azure Emulator 180 | ecf/ 181 | rcf/ 182 | 183 | # Windows Store app package directories and files 184 | AppPackages/ 185 | BundleArtifacts/ 186 | Package.StoreAssociation.xml 187 | _pkginfo.txt 188 | 189 | # Visual Studio cache files 190 | # files ending in .cache can be ignored 191 | *.[Cc]ache 192 | # but keep track of directories ending in .cache 193 | !*.[Cc]ache/ 194 | 195 | # Others 196 | ClientBin/ 197 | ~$* 198 | *~ 199 | *.dbmdl 200 | *.dbproj.schemaview 201 | *.jfm 202 | *.pfx 203 | *.publishsettings 204 | orleans.codegen.cs 205 | 206 | # Since there are multiple workflows, uncomment next line to ignore bower_components 207 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 208 | #bower_components/ 209 | 210 | # RIA/Silverlight projects 211 | Generated_Code/ 212 | 213 | # Backup & report files from converting an old project file 214 | # to a newer Visual Studio version. Backup files are not needed, 215 | # because we have git ;-) 216 | _UpgradeReport_Files/ 217 | Backup*/ 218 | UpgradeLog*.XML 219 | UpgradeLog*.htm 220 | 221 | # SQL Server files 222 | *.mdf 223 | *.ldf 224 | *.ndf 225 | 226 | # Business Intelligence projects 227 | *.rdl.data 228 | *.bim.layout 229 | *.bim_*.settings 230 | 231 | # Microsoft Fakes 232 | FakesAssemblies/ 233 | 234 | # GhostDoc plugin setting file 235 | *.GhostDoc.xml 236 | 237 | # Node.js Tools for Visual Studio 238 | .ntvs_analysis.dat 239 | node_modules/ 240 | 241 | # Typescript v1 declaration files 242 | typings/ 243 | 244 | # Visual Studio 6 build log 245 | *.plg 246 | 247 | # Visual Studio 6 workspace options file 248 | *.opt 249 | 250 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 251 | *.vbw 252 | 253 | # Visual Studio LightSwitch build output 254 | **/*.HTMLClient/GeneratedArtifacts 255 | **/*.DesktopClient/GeneratedArtifacts 256 | **/*.DesktopClient/ModelManifest.xml 257 | **/*.Server/GeneratedArtifacts 258 | **/*.Server/ModelManifest.xml 259 | _Pvt_Extensions 260 | 261 | # Paket dependency manager 262 | .paket/paket.exe 263 | paket-files/ 264 | 265 | # FAKE - F# Make 266 | .fake/ 267 | 268 | # JetBrains Rider 269 | .idea/ 270 | *.sln.iml 271 | 272 | # CodeRush 273 | .cr/ 274 | 275 | # Python Tools for Visual Studio (PTVS) 276 | __pycache__/ 277 | *.pyc 278 | 279 | # Cake - Uncomment if you are using it 280 | # tools/** 281 | # !tools/packages.config 282 | 283 | # Telerik's JustMock configuration file 284 | *.jmconfig 285 | 286 | # BizTalk build output 287 | *.btp.cs 288 | *.btm.cs 289 | *.odx.cs 290 | *.xsd.cs 291 | *.json 292 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 B D 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinyJitHook 2 | JIT Hook for: 3 | * .NET 2.0-4.6+ with x86 and x64 support. 4 | * .NET 2.0-3.5 does not support exception handlers. 5 | * .NET Core x.x-2.1 (tested) with x86 and x64 support for Windows devices only (WinAPI used). 6 | 7 | The main purpose of this JIT Hook is to demonstrate how someone can change method code at runtime. 8 | Unlike some other libraries out there, this does not edit native code after the original compile method has run. 9 | It edits the IL code before it is sent to be compiled. 10 | 11 | There are a few helper classes to create IL code, GetBytes/GetInstructions extension methods will allow you to read instructions, 12 | edit then write them to bytes. The overhead created by libraries such as dnlib is too great, the idea was to keep it tiny and simple. 13 | 14 | An example: a program has a serial key that users must enter, but decompilation can reveal the serial key's value or algorithm that checks the serial. 15 | Editing the check method through JIT can change the way the check happens without showing the real IL code to decompilers. 16 | 17 | Of course, the more famous example would be anti-tamper where method bodies are restored as needed through a JIT hook. 18 | 19 | 20 | # Usage 21 | For .NET 4.0+ support you must compile the project **with** the conditional compilation symbol "NET4" as well as change the target framework. 22 | 23 | For .NET 2.0-3.5 support you must compile the project **without** the symbol as well as change the target framework. 24 | 25 | For .NET Core support you must compile the project **with** the conditional compilation symbol "NET4". Tested with .NET Core 2.0 and 2.1. 26 | 27 | 28 | # SJITHook 29 | A lot of the structures have been changed from the original SJITHook presented quite some time ago. 30 | These changes were made to improve the original (and be more of a reflection of the present JIT implementation). 31 | If you are going to use the structures presented in this project; please note that they will change, and may not be entirely correct. 32 | I have changed them to work for test programs, not for everything out there in this world. 33 | 34 | 35 | # Credits 36 | * yck - ConfuserEx - Anti-Tamper JIT and Confuser 1.9 Anti-Tamper JIT x64 .NET 2.0 37 | * RebelDotNet - Good papers on JIT 38 | * .NET Core CLR 39 | * 0xd4d - Extra section reader 40 | -------------------------------------------------------------------------------- /TinyJitHook.Core.TestApp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TinyJitHook.Core.TestApp 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | Console.WriteLine("Hello World!"); 10 | Main2(); 11 | } 12 | static void Main2() 13 | { 14 | try 15 | { 16 | Console.WriteLine("Hello World2!"); 17 | Main3(); 18 | } 19 | catch (Exception ex) 20 | { 21 | Console.WriteLine($"Error! {ex}"); 22 | } 23 | } 24 | static void Main3() 25 | { 26 | Console.WriteLine("Hello World3!"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /TinyJitHook.Core.TestApp/TinyJitHook.Core.TestApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | AnyCPU;x64;x86 7 | TinyJitHook.Core.TestApp.Program 8 | 9 | 10 | 11 | DEBUG;TRACE 12 | true 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /TinyJitHook.Core/Directory.Build.targets: -------------------------------------------------------------------------------- 1 |  2 | 7 | $(MSBuildProgramFiles32)\dotnet\dotnet 8 | $(ProgramW6432)\dotnet\dotnet 9 | 10 | -------------------------------------------------------------------------------- /TinyJitHook.Core/ExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using TinyJitHook.Core.Extensions; 7 | 8 | namespace TinyJitHook.Core 9 | { 10 | // Credits: https://github.com/0xd4d/dnlib 11 | // License: https://github.com/0xd4d/dnlib/blob/master/LICENSE.txt 12 | 13 | /// 14 | /// A CIL method exception handler 15 | /// 16 | public sealed class ExceptionHandler 17 | { 18 | /// 19 | /// First instruction of try block 20 | /// 21 | public Instruction TryStart; 22 | public uint TryStartRaw; 23 | 24 | /// 25 | /// One instruction past the end of try block or null if it ends at the end 26 | /// of the method. 27 | /// 28 | public Instruction TryEnd; 29 | public uint TryEndRaw; 30 | 31 | /// 32 | /// Start of filter handler or null if none. The end of filter handler is 33 | /// always . 34 | /// 35 | public Instruction FilterStart; 36 | public uint FilterStartRaw; 37 | 38 | /// 39 | /// First instruction of try handler block 40 | /// 41 | public Instruction HandlerStart; 42 | public uint HandlerStartRaw; 43 | 44 | /// 45 | /// One instruction past the end of try handler block or null if it ends at the end 46 | /// of the method. 47 | /// 48 | public Instruction HandlerEnd; 49 | public uint HandlerEndRaw; 50 | 51 | /// 52 | /// Type of exception handler clause 53 | /// 54 | public ExceptionHandlerType HandlerType; 55 | 56 | public uint CatchTypeToken; 57 | 58 | /// 59 | /// Default constructor 60 | /// 61 | public ExceptionHandler() 62 | { 63 | } 64 | 65 | public static Instruction GetInstruction(uint off, List body) 66 | { 67 | return body.FirstOrDefault(inst => inst.Offset == off); 68 | } 69 | 70 | public static uint GetOffset(Instruction inst, List body, List recalcBody) 71 | { 72 | // TODO: Index of does not work with the recalculated body, the instruction is different 73 | // Custom comparator!? 74 | int index1 = body.IndexOf(inst); 75 | if (index1 < 0) 76 | { 77 | return uint.MaxValue; 78 | } 79 | 80 | int index2 = recalcBody.IndexOf(inst); 81 | if (index2 < 0) 82 | { 83 | // Hack to fix this, rely on the first index. 84 | index2 = index1; 85 | } 86 | 87 | return body[index1].Offset == 0 ? (uint)recalcBody[index2].Offset : (uint)body[index1].Offset; 88 | } 89 | 90 | public void ReadBig(BinaryReader r, List body) 91 | { 92 | HandlerType = (ExceptionHandlerType)r.ReadUInt32(); 93 | uint offset = r.ReadUInt32(); 94 | TryStart = GetInstruction(offset, body); 95 | TryStartRaw = offset; 96 | 97 | var off2 = offset + r.ReadUInt32(); 98 | TryEnd = GetInstruction(off2, body); 99 | TryEndRaw = off2; 100 | 101 | offset = r.ReadUInt32(); 102 | HandlerStart = GetInstruction(offset, body); 103 | HandlerStartRaw = offset; 104 | 105 | var off3 = offset + r.ReadUInt32(); 106 | HandlerEnd = GetInstruction(off3, body); 107 | HandlerEndRaw = off3; 108 | 109 | if (HandlerType == ExceptionHandlerType.Catch) 110 | { 111 | CatchTypeToken = r.ReadUInt32(); 112 | } 113 | else if (HandlerType == ExceptionHandlerType.Filter) 114 | { 115 | var off4 = r.ReadUInt32(); 116 | FilterStart = GetInstruction(off4, body); 117 | FilterStartRaw = off4; 118 | } 119 | else 120 | { 121 | r.ReadUInt32(); 122 | } 123 | } 124 | 125 | public void ReadSmall(BinaryReader r, List body) 126 | { 127 | HandlerType = (ExceptionHandlerType)r.ReadUInt16(); 128 | uint offset = r.ReadUInt16(); 129 | TryStart = GetInstruction(offset, body); 130 | TryStartRaw = offset; 131 | 132 | var off2 = offset + r.ReadByte(); 133 | TryEnd = GetInstruction(off2, body); 134 | TryEndRaw = off2; 135 | 136 | offset = r.ReadUInt16(); 137 | HandlerStart = GetInstruction(offset, body); 138 | HandlerStartRaw = offset; 139 | 140 | var off3 = offset + r.ReadByte(); 141 | HandlerEnd = GetInstruction(off3, body); 142 | HandlerEndRaw = off3; 143 | 144 | if (HandlerType == ExceptionHandlerType.Catch) 145 | { 146 | CatchTypeToken = r.ReadUInt32(); 147 | } 148 | else if (HandlerType == ExceptionHandlerType.Filter) 149 | { 150 | var off4 = r.ReadUInt32(); 151 | FilterStart = GetInstruction(off4, body); 152 | FilterStartRaw = off4; 153 | } 154 | else 155 | { 156 | r.ReadUInt32(); 157 | } 158 | } 159 | 160 | public void WriteBig(BinaryWriter w, List body, List rBody) 161 | { 162 | w.Write((uint)HandlerType); 163 | 164 | uint offs1 = GetOffset(TryStart, body, rBody); 165 | uint offs2 = GetOffset(TryEnd, body, rBody); 166 | if (offs2 <= offs1) 167 | throw new Exception("Exception handler: TryEnd <= TryStart"); 168 | w.Write(offs1); 169 | w.Write(offs2 - offs1); 170 | 171 | offs1 = GetOffset(HandlerStart, body, rBody); 172 | offs2 = GetOffset(HandlerEnd, body, rBody); 173 | if (offs2 <= offs1) 174 | throw new Exception("Exception handler: HandlerEnd <= HandlerStart"); 175 | w.Write(offs1); 176 | w.Write(offs2 - offs1); 177 | 178 | if (HandlerType == ExceptionHandlerType.Catch) 179 | w.Write(CatchTypeToken); 180 | else if (HandlerType == ExceptionHandlerType.Filter) 181 | w.Write(GetOffset(FilterStart, body, rBody)); 182 | else 183 | w.Write(0); 184 | } 185 | public void WriteSmall(BinaryWriter w, List body, List rBody) 186 | { 187 | w.Write((ushort)HandlerType); 188 | 189 | uint offs1 = GetOffset(TryStart, body, rBody); 190 | uint offs2 = GetOffset(TryEnd, body, rBody); 191 | if (offs2 <= offs1) 192 | throw new Exception("Exception handler: TryEnd <= TryStart"); 193 | w.Write((ushort)offs1); 194 | w.Write((byte)(offs2 - offs1)); 195 | 196 | offs1 = GetOffset(HandlerStart, body, rBody); 197 | offs2 = GetOffset(HandlerEnd, body, rBody); 198 | if (offs2 <= offs1) 199 | throw new Exception("Exception handler: HandlerEnd <= HandlerStart"); 200 | w.Write((ushort)offs1); 201 | w.Write((byte)(offs2 - offs1)); 202 | 203 | if (HandlerType == ExceptionHandlerType.Catch) 204 | w.Write(CatchTypeToken); 205 | else if (HandlerType == ExceptionHandlerType.Filter) 206 | w.Write(GetOffset(FilterStart, body, rBody)); 207 | else 208 | w.Write(0); 209 | } 210 | } 211 | 212 | /// 213 | /// Type of exception handler. See CorHdr.h/CorExceptionFlag 214 | /// 215 | [Flags] 216 | public enum ExceptionHandlerType 217 | { 218 | /// 219 | Catch = 0x0000, 220 | /// 221 | Filter = 0x0001, 222 | /// 223 | Finally = 0x0002, 224 | /// 225 | Fault = 0x0004, 226 | /// 227 | Duplicated = 0x0008, 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /TinyJitHook.Core/Extensions/ExceptionHandlerByteHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using TinyJitHook.Core; 6 | using TinyJitHook.Core.SJITHook; 7 | 8 | namespace TinyJitHook.Core.Extensions 9 | { 10 | public static class ExceptionHandlerByteHelper 11 | { 12 | public static List GetExceptionClauses(this byte[] data, 13 | List relatedMethodBody) 14 | { 15 | List ehList = data.GetExceptionHandlers(relatedMethodBody); 16 | List exceptionHandlers = new List(); 17 | foreach (var eh in ehList) 18 | { 19 | exceptionHandlers.Add(new Data.CorInfoEhClause 20 | { 21 | Flags = (uint)eh.HandlerType, 22 | TryOffset = eh.TryStartRaw, 23 | TryLength = eh.TryEndRaw - eh.TryStartRaw, 24 | HandlerOffset = eh.HandlerStartRaw, 25 | HandlerLength = eh.HandlerEndRaw - eh.HandlerStartRaw, 26 | ClassTokenOrFilterOffset = eh.CatchTypeToken 27 | }); 28 | } 29 | 30 | return exceptionHandlers; 31 | } 32 | public static List GetExceptionHandlers( 33 | this byte[] data, List relatedMethodBody) 34 | { 35 | var ret = new List(); 36 | 37 | var ehReader = new BinaryReader(new MemoryStream(data)); 38 | 39 | byte b = ehReader.ReadByte(); 40 | if ((b & 0x3F) != 1) 41 | return new List(); // Not exception handler clauses 42 | 43 | ret.AddRange((b & 0x40) != 0 44 | ? ReadBigExceptionHandlers(ehReader, relatedMethodBody) 45 | : ReadSmallExceptionHandlers(ehReader, relatedMethodBody)); 46 | 47 | return ret; 48 | } 49 | 50 | public static byte[] GetExceptionHandlerBytes(this List exceptionHandlers, 51 | List relatedMethodBody) 52 | { 53 | // Recalculate offsets 54 | byte[] tmpBody = relatedMethodBody.GetInstructionBytes(); 55 | List rBody = tmpBody.GetInstructions().ToList(); 56 | 57 | byte[] extraSections; 58 | if (NeedBigExceptionClauses(exceptionHandlers, relatedMethodBody, rBody)) 59 | extraSections = WriteBigExceptionClauses(exceptionHandlers, relatedMethodBody, rBody); 60 | else 61 | extraSections = WriteSmallExceptionClauses(exceptionHandlers, relatedMethodBody, rBody); 62 | return extraSections; 63 | 64 | } 65 | 66 | private static byte[] WriteBigExceptionClauses(List exceptionHandlers, 67 | List relatedMethodBody, List rBody) 68 | { 69 | const int maxExceptionHandlers = (0x00FFFFFF - 4) / 24; 70 | int numExceptionHandlers = exceptionHandlers.Count; 71 | if (numExceptionHandlers > maxExceptionHandlers) throw new Exception("Too many exception handlers"); 72 | 73 | byte[] data = new byte[numExceptionHandlers * 24 + 4]; 74 | using (var ms = new MemoryStream(data)) 75 | using (var writer = new BinaryWriter(ms)) 76 | { 77 | writer.Write((uint)numExceptionHandlers * 24 + 4 << 8 | 0x41); 78 | for (var i = 0; i < numExceptionHandlers; i++) 79 | { 80 | var eh = exceptionHandlers[i]; 81 | eh.WriteBig(writer, relatedMethodBody, rBody); 82 | } 83 | 84 | if (writer.BaseStream.Position != data.Length) 85 | throw new InvalidOperationException(); 86 | } 87 | 88 | return data; 89 | } 90 | 91 | private static byte[] WriteSmallExceptionClauses(List exceptionHandlers, 92 | List relatedMethodBody, List rBody) 93 | { 94 | const int maxExceptionHandlers = (0xFF - 4) / 12; 95 | int numExceptionHandlers = exceptionHandlers.Count; 96 | if (numExceptionHandlers > maxExceptionHandlers) throw new Exception("Too many exception handlers"); 97 | 98 | byte[] data = new byte[numExceptionHandlers * 12 + 4]; 99 | using (var ms = new MemoryStream(data)) 100 | using (var writer = new BinaryWriter(ms)) 101 | { 102 | writer.Write((uint)numExceptionHandlers * 12 + 4 << 8 | 1); 103 | for (var i = 0; i < numExceptionHandlers; i++) 104 | { 105 | var eh = exceptionHandlers[i]; 106 | eh.WriteSmall(writer, relatedMethodBody, rBody); 107 | } 108 | 109 | if (writer.BaseStream.Position != data.Length) 110 | throw new InvalidOperationException(); 111 | } 112 | 113 | return data; 114 | } 115 | 116 | private static List ReadBigExceptionHandlers( 117 | BinaryReader ehReader, List body) 118 | { 119 | List ret = new List(); 120 | ehReader.BaseStream.Position--; 121 | int num = (ushort)((ehReader.ReadUInt32() >> 8) / 24); 122 | for (var i = 0; i < num; i++) 123 | { 124 | var eh = new ExceptionHandler(); 125 | eh.ReadBig(ehReader, body); 126 | ret.Add(eh); 127 | } 128 | 129 | return ret; 130 | } 131 | 132 | private static List ReadSmallExceptionHandlers( 133 | BinaryReader ehReader, List body) 134 | { 135 | List ret = new List(); 136 | int num = (ushort)((uint)ehReader.ReadByte() / 12); 137 | ehReader.BaseStream.Position += 2; 138 | for (var i = 0; i < num; i++) 139 | { 140 | var eh = new ExceptionHandler(); 141 | eh.ReadSmall(ehReader, body); 142 | ret.Add(eh); 143 | } 144 | 145 | return ret; 146 | } 147 | 148 | private static bool NeedBigExceptionClauses(List exceptionHandlers, List body, 149 | List rBody) 150 | { 151 | // Size must fit in a byte, and since one small exception record is 12 bytes 152 | // and header is 4 bytes: x*12+4 <= 255 ==> x <= 20 153 | if (exceptionHandlers.Count > 20) 154 | return true; 155 | 156 | foreach (var eh in exceptionHandlers) 157 | { 158 | if (!FitsInSmallExceptionClause(eh.TryStart, eh.TryEnd, body, rBody)) 159 | return true; 160 | if (!FitsInSmallExceptionClause(eh.HandlerStart, eh.HandlerEnd, body, rBody)) 161 | return true; 162 | } 163 | 164 | return false; 165 | } 166 | 167 | private static bool FitsInSmallExceptionClause(Instruction start, Instruction end, List body, 168 | List rBody) 169 | { 170 | uint offs1 = ExceptionHandler.GetOffset(start, body, rBody); 171 | uint offs2 = ExceptionHandler.GetOffset(end, body, rBody); 172 | if (offs2 < offs1) 173 | return false; 174 | return offs1 <= ushort.MaxValue && offs2 - offs1 <= byte.MaxValue; 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /TinyJitHook.Core/Extensions/InstructionByteHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using TinyJitHook.Core; 4 | 5 | namespace TinyJitHook.Core.Extensions 6 | { 7 | public static class InstructionByteHelper 8 | { 9 | public static List GetInstructions(this byte[] bytes) 10 | { 11 | var ret = new List(); 12 | using (MemoryStream ms = new MemoryStream(bytes)) 13 | using (BinaryReader r = new BinaryReader(ms)) 14 | { 15 | while (r.BaseStream.Position < bytes.Length) 16 | { 17 | Instruction instruction = new Instruction { Offset = (int)r.BaseStream.Position }; 18 | 19 | short code = r.ReadByte(); 20 | if (code == 0xfe) 21 | { 22 | code = (short)(r.ReadByte() | 0xfe00); 23 | } 24 | 25 | instruction.OpCode = code.GetOpCode(); 26 | instruction.Read(r); 27 | 28 | ret.Add(instruction); 29 | } 30 | } 31 | 32 | return ret; 33 | } 34 | 35 | public static byte[] GetInstructionBytes(this List instructions) 36 | { 37 | using (MemoryStream ms = new MemoryStream()) 38 | using (BinaryWriter w = new BinaryWriter(ms)) 39 | { 40 | foreach (var inst in instructions) 41 | { 42 | if (inst.OpCode.Size == 1) 43 | { 44 | w.Write((byte)inst.OpCode.Value); 45 | } 46 | else 47 | { 48 | w.Write(inst.OpCode.Value); 49 | } 50 | 51 | inst.Write(w); 52 | } 53 | 54 | return ms.ToArray(); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /TinyJitHook.Core/Extensions/JitHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Runtime.CompilerServices; 5 | using TinyJitHook.Core.SJITHook; 6 | 7 | namespace TinyJitHook.Core.Extensions 8 | { 9 | public static class JitHelper 10 | { 11 | public static void PrepareAssemblies(this AppDomain domain) 12 | { 13 | foreach (var asm in domain.GetAssemblies()) 14 | { 15 | asm.PrepareMethods(); 16 | } 17 | } 18 | public static void PrepareMethods(this Assembly asm) 19 | { 20 | foreach (Type t in asm.GetTypes()) 21 | { 22 | if (t.ContainsGenericParameters || t.IsGenericType) 23 | continue; 24 | 25 | foreach (MethodInfo mi in t.GetMethods()) 26 | { 27 | if (mi.IsAbstract || mi.IsGenericMethod || mi.IsGenericMethodDefinition) 28 | continue; 29 | if (mi.DeclaringType != null && 30 | (mi.DeclaringType.IsGenericType || mi.DeclaringType.IsGenericTypeDefinition)) 31 | continue; 32 | RuntimeHelpers.PrepareMethod(mi.MethodHandle); 33 | } 34 | 35 | foreach (ConstructorInfo ci in t.GetConstructors()) 36 | { 37 | if (ci.IsAbstract) 38 | continue; 39 | RuntimeHelpers.PrepareMethod(ci.MethodHandle); 40 | } 41 | } 42 | } 43 | public static void PrepareOriginalCompileGetter(this IJitHook hook, bool is64Bit) 44 | { 45 | // Preload the original method property getter 46 | Type hookType = hook.GetType(); 47 | Type[] genParams = hookType.GetGenericArguments(); 48 | RuntimeTypeHandle[] genRTs = new RuntimeTypeHandle[genParams.Length]; 49 | 50 | for (int i = 0; i < genParams.Length; i++) 51 | { 52 | genRTs[i] = genParams[i].TypeHandle; 53 | } 54 | 55 | RuntimeHelpers.PrepareMethod( 56 | hookType.GetMethod($"get_OriginalCompileMethod{(is64Bit ? "64" : "32")}").MethodHandle, genRTs); 57 | } 58 | public static IntPtr[] GetAssemblyScopes(this AppDomain domain) 59 | { 60 | Assembly[] asms = domain.GetAssemblies(); 61 | var moduleScopes = new IntPtr[asms.Length]; 62 | for (int i = 0; i < asms.Length; i++) 63 | { 64 | // The invoking assembly is generally at index 1. 65 | moduleScopes[i] = asms[i].ManifestModule.GetScope(); 66 | } 67 | 68 | return moduleScopes; 69 | } 70 | public static Dictionary GetScopeMap(this AppDomain domain) 71 | { 72 | var moduleScopes = new Dictionary(); 73 | foreach (var assembly in domain.GetAssemblies()) 74 | { 75 | moduleScopes.Add(assembly.ManifestModule.GetScope(), assembly); 76 | } 77 | 78 | return moduleScopes; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /TinyJitHook.Core/Extensions/OpCodeShortHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection.Emit; 3 | 4 | namespace TinyJitHook.Core.Extensions 5 | { 6 | public static class OpCodeShortHelper 7 | { 8 | private static readonly Dictionary _opCodes = new Dictionary(); 9 | 10 | static OpCodeShortHelper() 11 | { 12 | Initialize(); 13 | } 14 | 15 | 16 | public static OpCode GetOpCode(this short value) 17 | { 18 | return _opCodes[value]; 19 | } 20 | 21 | 22 | private static void Initialize() 23 | { 24 | foreach (var fieldInfo in typeof(OpCodes).GetFields()) 25 | { 26 | var opCode = (OpCode)fieldInfo.GetValue(null); 27 | 28 | _opCodes.Add(opCode.Value, opCode); 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /TinyJitHook.Core/Extensions/PathHelper.cs: -------------------------------------------------------------------------------- 1 | namespace TinyJitHook.Core.Extensions 2 | { 3 | public static class PathHelper 4 | { 5 | /// 6 | /// Get a custom branded file path, defaults to _dumped 7 | /// 8 | /// The filepath to brand. 9 | /// The brand to give it (goes after the file name, but before the extension. 10 | /// A full file path with the custom brand (C:\something_brandhere.exe). 11 | public static string GetFilePath(this string filePath, string brand = "_dumped") 12 | { 13 | string outFile = filePath; 14 | int index = outFile.LastIndexOf('.'); 15 | if (index != -1) 16 | { 17 | outFile = outFile.Insert(index, brand); 18 | } 19 | return outFile; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TinyJitHook.Core/Extensions/ReflectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace TinyJitHook.Core.Extensions 5 | { 6 | /// 7 | /// A general class to help with many things related to reflection. 8 | /// 9 | public static class ReflectionHelper 10 | { 11 | /// 12 | /// Get a module scope. 13 | /// 14 | /// The module to get the scope of. 15 | /// The module's scope. 16 | public static IntPtr GetScope(this Module mod) 17 | { 18 | var obj = mod.ModuleHandle.GetFieldValue("m_ptr"); 19 | if (obj is IntPtr) 20 | { 21 | return (IntPtr)obj; 22 | } 23 | if (obj.GetType().ToString() == "System.Reflection.RuntimeModule") 24 | { 25 | return obj.GetFieldValue("m_pData"); 26 | } 27 | 28 | throw new Exception("Cannot get scope of module."); 29 | } 30 | /// 31 | /// Get a field value from a field using reflection. 32 | /// 33 | /// Type of the field (the return type). 34 | /// The object to get the field from. 35 | /// The field name of the field to retrieve value of. 36 | /// The value of the field casted to T. 37 | public static T GetFieldValue(this object obj, string fieldName) 38 | { 39 | if (obj == null) 40 | throw new ArgumentNullException("obj"); 41 | var rtType = obj.GetType(); 42 | // var fields = rtType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 43 | var field = rtType.GetField(fieldName, BindingFlags.Public | 44 | BindingFlags.NonPublic | 45 | BindingFlags.Instance); 46 | 47 | if (field == null) 48 | throw new ArgumentException("fieldName", "No such field was found."); 49 | 50 | if (!typeof(T).IsAssignableFrom(field.FieldType)) 51 | throw new InvalidOperationException("Field type and requested type are not compatible."); 52 | 53 | return (T)field.GetValue(obj); 54 | } 55 | /// 56 | /// Get a field value from a field of a specific type using reflection. 57 | /// 58 | /// Type of the field (the return type). 59 | /// The object. 60 | /// The field name of the field to retrieve value of. 61 | /// The type to use to get the field from. 62 | /// The value of the field casted to T 63 | public static T GetFieldValue(this object obj, string fieldName, Type theType) 64 | { 65 | if (obj == null) 66 | throw new ArgumentNullException("obj"); 67 | 68 | // var fields = theType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 69 | var field = theType.GetField(fieldName, BindingFlags.Public | 70 | BindingFlags.NonPublic | 71 | BindingFlags.Instance); 72 | 73 | if (field == null) 74 | throw new ArgumentException("fieldName", "No such field was found."); 75 | 76 | if (!typeof(T).IsAssignableFrom(field.FieldType)) 77 | throw new InvalidOperationException("Field type and requested type are not compatible."); 78 | 79 | return (T)field.GetValue(obj); 80 | } 81 | /// 82 | /// Set a field's value using reflection. 83 | /// 84 | /// The type instance that holds the field to set the value of. 85 | /// The field name. 86 | /// The data to set the field to, must be the field type. 87 | public static void SetFieldValue(this object obj, string fieldName, object data) 88 | { 89 | if (obj == null) 90 | throw new ArgumentNullException("obj"); 91 | 92 | var field = obj.GetType().GetField(fieldName, BindingFlags.Public | 93 | BindingFlags.NonPublic | 94 | BindingFlags.Instance); 95 | 96 | if (field == null) 97 | throw new ArgumentException("fieldName", "No such field was found."); 98 | 99 | field.SetValue(obj, data); 100 | } 101 | 102 | /// 103 | /// Get a method in a type with the flags specified, will work with internal and private types. 104 | /// 105 | /// The full name (including namespace) of the type. 106 | /// The method name to retrieve. 107 | /// The binding flags to use to find the method on the type. 108 | /// Whether or not to immediately return when found. 109 | /// The method with the name supplied. 110 | public static MethodInfo GetMethod(this string type, string method, BindingFlags flags, bool breakOnFind = true) 111 | { 112 | MethodInfo found = null; 113 | foreach (MethodInfo mInfo in typeof(Assembly).Assembly.GetType(type) 114 | .GetMethods(flags)) 115 | { 116 | if (mInfo.Name == method) 117 | { 118 | found = mInfo; 119 | if (breakOnFind) 120 | { 121 | break; 122 | } 123 | } 124 | } 125 | return found; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /TinyJitHook.Core/HookHelpers/HookHelper32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using TinyJitHook.Core.Extensions; 4 | using TinyJitHook.Core.Models; 5 | using TinyJitHook.Core.SJITHook; 6 | 7 | namespace TinyJitHook.Core.HookHelpers 8 | { 9 | /// 10 | /// A 32 bit hook helper that wraps around SJITHook's implementation. 11 | /// 12 | public class HookHelper32 : IHookHelper 13 | { 14 | /// 15 | /// The original CompileMethod. 16 | /// 17 | public Data.CompileMethodDel Original; 18 | 19 | /// 20 | /// The loaded assembly. 21 | /// 22 | public Assembly LoadedAssembly { get; private set; } 23 | 24 | /// 25 | /// The underlying JIT hook used. 26 | /// 27 | public IJitHook Hook { get; private set; } 28 | 29 | /// 30 | /// The loaded module's scope. 31 | /// 32 | public IntPtr ModuleScope { get; private set; } 33 | 34 | /// 35 | /// Initialize a new 32 bit hook for JIT on the supplied assembly with the callback CompileMethod. 36 | /// Will determine the runtime version (and JIT hook to use). 37 | /// 38 | /// The assembly to wrap 39 | /// The CompileMethod to call instead of the original. 40 | public HookHelper32(Assembly asm, Data.CompileMethodDel hookedCompileMethod32) 41 | { 42 | 43 | LoadedAssembly = asm; 44 | ModuleScope = LoadedAssembly.ManifestModule.GetScope(); 45 | 46 | // .NET 4.0+ 47 | Hook = new JITHook(hookedCompileMethod32); 48 | 49 | Original = Hook.OriginalCompileMethod32; 50 | } 51 | 52 | /// 53 | /// Apply the JIT hook and load the assembly (does invoke the module static constructor!). 54 | /// 55 | /// Whether or not the JIT hook was applied. 56 | public bool Apply() 57 | { 58 | return Hook.Hook(); 59 | } 60 | 61 | /// 62 | /// Remove the JIT hook and restore the original. 63 | /// 64 | /// Whether or not the JIT hook was successfully unhooked. 65 | public bool Remove() 66 | { 67 | return Hook.UnHook(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /TinyJitHook.Core/HookHelpers/HookHelper64.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using TinyJitHook.Core.Extensions; 4 | using TinyJitHook.Core.Models; 5 | using TinyJitHook.Core.SJITHook; 6 | 7 | namespace TinyJitHook.Core.HookHelpers 8 | { 9 | /// 10 | /// A 64 bit hook helper that wraps around SJITHook's implementation. 11 | /// 12 | public class HookHelper64 : IHookHelper 13 | { 14 | /// 15 | /// The original CompileMethod. 16 | /// 17 | public Data.CompileMethodDel64 Original; 18 | 19 | /// 20 | /// The loaded assembly. 21 | /// 22 | public Assembly LoadedAssembly { get; private set; } 23 | 24 | /// 25 | /// The underlying JIT hook used. 26 | /// 27 | public IJitHook Hook { get; private set; } 28 | 29 | /// 30 | /// The loaded module's scope. 31 | /// 32 | public IntPtr ModuleScope { get; private set; } 33 | 34 | /// 35 | /// Initialize a new 64 bit hook for JIT on the supplied assembly with the callback CompileMethod. 36 | /// Will determine the runtime version (and JIT hook to use). 37 | /// 38 | /// The assembly to wrap. 39 | /// The CompileMethod to call instead of the original. 40 | public HookHelper64(Assembly asm, Data.CompileMethodDel64 hookedCompileMethod64) 41 | { 42 | LoadedAssembly = asm; 43 | ModuleScope = LoadedAssembly.ManifestModule.GetScope(); 44 | 45 | // .NET 4.0+ 46 | Hook = new JITHook64(hookedCompileMethod64); 47 | Original = Hook.OriginalCompileMethod64; 48 | } 49 | 50 | /// 51 | /// Apply the JIT hook and load the assembly (does invoke the module static constructor!). 52 | /// 53 | /// Whether or not the JIT hook was applied. 54 | public bool Apply() 55 | { 56 | return Hook.Hook(); 57 | } 58 | 59 | /// 60 | /// Remove the JIT hook and restore the original. 61 | /// 62 | /// Whether or not the JIT hook was successfully unhooked. 63 | public bool Remove() 64 | { 65 | return Hook.UnHook(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /TinyJitHook.Core/Instruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection.Emit; 4 | 5 | namespace TinyJitHook.Core 6 | { 7 | public sealed class Instruction 8 | { 9 | public int Offset { get; set; } 10 | 11 | 12 | public OpCode OpCode { get; set; } 13 | 14 | public object Data { get; set; } 15 | 16 | public static Instruction Create(OpCode opcode, object data = null) 17 | { 18 | var inst = new Instruction 19 | { 20 | OpCode = opcode, 21 | Data = data 22 | }; 23 | return inst; 24 | } 25 | 26 | public void Read(BinaryReader r) 27 | { 28 | switch (OpCode.OperandType) 29 | { 30 | case OperandType.InlineBrTarget: 31 | Data = r.ReadInt32(); 32 | break; 33 | 34 | case OperandType.InlineField: 35 | Data = r.ReadInt32(); 36 | break; 37 | 38 | case OperandType.InlineI: 39 | Data = r.ReadInt32(); 40 | break; 41 | 42 | case OperandType.InlineI8: 43 | Data = r.ReadInt64(); 44 | break; 45 | 46 | case OperandType.InlineMethod: 47 | Data = r.ReadInt32(); 48 | break; 49 | 50 | case OperandType.InlineNone: 51 | break; 52 | 53 | case OperandType.InlineR: 54 | Data = r.ReadDouble(); 55 | break; 56 | 57 | case OperandType.InlineSig: 58 | Data = r.ReadInt32(); 59 | break; 60 | 61 | case OperandType.InlineString: 62 | Data = r.ReadInt32(); 63 | break; 64 | 65 | case OperandType.InlineSwitch: 66 | int count = r.ReadInt32() + 1; 67 | Data = r.ReadBytes(4 * count); 68 | break; 69 | 70 | case OperandType.InlineTok: 71 | Data = r.ReadInt32(); 72 | break; 73 | 74 | case OperandType.InlineType: 75 | Data = r.ReadInt32(); 76 | break; 77 | 78 | case OperandType.InlineVar: 79 | Data = r.ReadBytes(2); 80 | break; 81 | 82 | case OperandType.ShortInlineBrTarget: 83 | Data = r.ReadByte(); 84 | break; 85 | 86 | case OperandType.ShortInlineI: 87 | Data = r.ReadByte(); 88 | break; 89 | 90 | case OperandType.ShortInlineR: 91 | Data = r.ReadSingle(); 92 | break; 93 | 94 | case OperandType.ShortInlineVar: 95 | Data = r.ReadByte(); 96 | break; 97 | 98 | default: 99 | throw new NotImplementedException(); 100 | } 101 | 102 | } 103 | 104 | public void Write(BinaryWriter w) 105 | { 106 | switch (OpCode.OperandType) 107 | { 108 | case OperandType.InlineBrTarget: 109 | w.Write((int)Data); 110 | break; 111 | 112 | case OperandType.InlineField: 113 | w.Write((int)Data); 114 | break; 115 | 116 | case OperandType.InlineI: 117 | w.Write((int)Data); 118 | break; 119 | 120 | case OperandType.InlineI8: 121 | w.Write((long)Data); 122 | break; 123 | 124 | case OperandType.InlineMethod: 125 | w.Write((int)Data); 126 | break; 127 | 128 | case OperandType.InlineNone: 129 | break; 130 | 131 | case OperandType.InlineR: 132 | w.Write((double)Data); 133 | break; 134 | 135 | case OperandType.InlineSig: 136 | w.Write((int)Data); 137 | break; 138 | 139 | case OperandType.InlineString: 140 | w.Write((int)Data); 141 | break; 142 | 143 | case OperandType.InlineSwitch: 144 | w.Write(((byte[])Data).Length / 4 - 1); 145 | w.Write((byte[])Data); 146 | break; 147 | 148 | case OperandType.InlineTok: 149 | w.Write((int)Data); 150 | break; 151 | 152 | case OperandType.InlineType: 153 | w.Write((int)Data); 154 | break; 155 | 156 | case OperandType.InlineVar: 157 | w.Write((byte[])Data); 158 | break; 159 | 160 | case OperandType.ShortInlineBrTarget: 161 | w.Write((byte)Data); 162 | break; 163 | 164 | case OperandType.ShortInlineI: 165 | w.Write((sbyte)Data); 166 | break; 167 | 168 | case OperandType.ShortInlineR: 169 | w.Write((float)Data); 170 | break; 171 | 172 | case OperandType.ShortInlineVar: 173 | w.Write((byte)Data); 174 | break; 175 | 176 | default: 177 | throw new NotImplementedException(); 178 | } 179 | 180 | } 181 | 182 | public override string ToString() 183 | { 184 | return $"{Offset:X4}: {OpCode} - {Data}"; 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /TinyJitHook.Core/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TinyJitHook.Core 4 | { 5 | public static class Logger 6 | { 7 | /// 8 | /// Whether or not to suppress info messages. 9 | /// 10 | public static bool SuppressInfo { get; set; } 11 | /// 12 | /// Whether or not to suppress warning messages. 13 | /// 14 | public static bool SuppressWarn { get; set; } 15 | /// 16 | /// Whether or not to suppress all logging. 17 | /// 18 | public static bool SuppressLogging { get; set; } 19 | 20 | /// 21 | /// Generic log to console with no color. 22 | /// 23 | /// The type (will print name of type) that is calling. 24 | /// The message to log. 25 | public static void Log(Type t, string msg) 26 | { 27 | if (SuppressLogging) 28 | { 29 | return; 30 | } 31 | string data = $"[{(t == null ? "GLOBAL" : t.Name)}] {msg}"; 32 | Console.WriteLine(data); 33 | } 34 | /// 35 | /// Log a success message () to console. 36 | /// 37 | /// The type (will print name of type) that is calling. 38 | /// The message to log. 39 | public static void LogSuccess(Type t, string msg) 40 | { 41 | Console.ForegroundColor = ConsoleColor.Green; 42 | Log(t, msg); 43 | Console.ResetColor(); 44 | } 45 | /// 46 | /// Log a warning message () to console. 47 | /// 48 | /// The type (will print name of type) that is calling. 49 | /// The message to log. 50 | public static void LogWarn(Type t, string msg) 51 | { 52 | if (SuppressWarn) 53 | { 54 | return; 55 | } 56 | Console.ForegroundColor = ConsoleColor.Yellow; 57 | Log(t, msg); 58 | Console.ResetColor(); 59 | } 60 | /// 61 | /// Log an info message () to console. 62 | /// 63 | /// The type (will print name of type) that is calling. 64 | /// The message to log. 65 | public static void LogInfo(Type t, string msg) 66 | { 67 | if (SuppressInfo) 68 | { 69 | return; 70 | } 71 | Console.ForegroundColor = ConsoleColor.Cyan; 72 | Log(t, msg); 73 | Console.ResetColor(); 74 | } 75 | /// 76 | /// Log an error message () to console. 77 | /// 78 | /// The type (will print name of type) that is calling. 79 | /// The message to log. 80 | public static void LogError(Type t, string msg) 81 | { 82 | Console.ForegroundColor = ConsoleColor.Red; 83 | Log(t, msg); 84 | Console.ResetColor(); 85 | } 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /TinyJitHook.Core/MainJitHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | using System.Threading; 7 | using TinyJitHook.Core.Extensions; 8 | using TinyJitHook.Core.HookHelpers; 9 | using TinyJitHook.Core.Models; 10 | using TinyJitHook.Core.SJITHook; 11 | 12 | namespace TinyJitHook.Core 13 | { 14 | public unsafe class MainJitHook 15 | { 16 | private const int GET_METHOD_DEF_FROM_METHOD_SLOT_INDEX = 112; // 112 seems promising 17 | private static MainJitHook _instance; 18 | private readonly IHookHelper _hookHelper; 19 | 20 | public readonly bool Is64Bit; 21 | public int EntryCount; 22 | 23 | private Dictionary _scopeMap; 24 | 25 | public delegate void ActionDelegate(RawArguments args, Assembly relatedAssembly, 26 | uint methodToken, ref byte[] ilBytes, ref byte[] ehBytes); 27 | 28 | #region Events 29 | 30 | private readonly AutoResetEvent _compileMethodResetEvent; 31 | private static object _jitCompileMethodLock = new object(); 32 | public event ActionDelegate OnCompileMethod; 33 | 34 | #endregion 35 | 36 | public class RawArguments 37 | { 38 | public IntPtr ThisPtr; 39 | public IntPtr CorJitInfo; 40 | public IntPtr MethodInfo; 41 | public Data.CorJitFlag Flags; 42 | 43 | public IntPtr NativeEntry; 44 | public IntPtr NativeSizeOfCode; 45 | } 46 | 47 | public MainJitHook(Assembly asm, bool is64Bit) 48 | { 49 | Is64Bit = is64Bit; 50 | EntryCount = 0; 51 | 52 | _instance = this; 53 | _compileMethodResetEvent = new AutoResetEvent(false); 54 | 55 | if (is64Bit) 56 | _hookHelper = new HookHelper64(asm, HookedCompileMethod64); 57 | else 58 | _hookHelper = new HookHelper32(asm, HookedCompileMethod32); 59 | 60 | _hookHelper.Hook.PrepareOriginalCompileGetter(Is64Bit); 61 | //Assembly.GetExecutingAssembly().PrepareMethods(); 62 | 63 | //AppDomain.CurrentDomain.PrepareAssemblies(); 64 | 65 | _scopeMap = AppDomain.CurrentDomain.GetScopeMap(); 66 | 67 | } 68 | 69 | public void Hook() 70 | { 71 | if (!_hookHelper.Apply()) throw new Exception("Hook could not be applied."); 72 | } 73 | 74 | public void Unhook() 75 | { 76 | if (!_hookHelper.Remove()) throw new Exception("Hook could not be removed."); 77 | } 78 | 79 | [MethodImpl(MethodImplOptions.NoInlining)] 80 | private static void OnCompileEventResetMethod(RawArguments args, Assembly assembly, uint methodToken, 81 | ref byte[] bytes, 82 | ref byte[] ehBytes) 83 | { 84 | _instance._compileMethodResetEvent.Set(); 85 | } 86 | 87 | [MethodImpl(MethodImplOptions.NoInlining)] 88 | private static int HookedCompileMethod32(IntPtr thisPtr, [In] IntPtr corJitInfo, 89 | [In] Data.CorMethodInfo* methodInfo, Data.CorJitFlag flags, 90 | [Out] IntPtr nativeEntry, [Out] IntPtr nativeSizeOfCode) 91 | { 92 | // THIS IS THE 32 BIT COMPILE METHOD. 93 | 94 | if (thisPtr == IntPtr.Zero) return 0; 95 | 96 | var o = _instance._hookHelper.Hook.OriginalCompileMethod32; 97 | 98 | _instance.EntryCount++; 99 | if (_instance.EntryCount > 1) 100 | { 101 | _instance.EntryCount--; 102 | return o(thisPtr, corJitInfo, methodInfo, flags, 103 | nativeEntry, nativeSizeOfCode); 104 | } 105 | 106 | 107 | var safeMethodInfo = new IntPtr((int*)methodInfo); 108 | #if NET4 109 | EHInfoHook exceptionHandlerHook = null; 110 | #endif 111 | 112 | #if NET4 113 | var functionPtr = Marshal.ReadIntPtr(Marshal.ReadIntPtr(corJitInfo), IntPtr.Size * GET_METHOD_DEF_FROM_METHOD_SLOT_INDEX); 114 | var getMethodDef = (Data.GetMethodDefFromMethodDel)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(Data.GetMethodDefFromMethodDel)); 115 | var token = getMethodDef(corJitInfo, methodInfo->ftn); 116 | #else 117 | var token = (uint)(0x06000000 | *(ushort*)methodInfo->ftn); 118 | #endif 119 | 120 | lock (_jitCompileMethodLock) 121 | { 122 | 123 | Assembly relatedAssembly = null; 124 | if (!_instance._scopeMap.ContainsKey(methodInfo->scope)) 125 | _instance._scopeMap = AppDomain.CurrentDomain.GetScopeMap(); 126 | if (_instance._scopeMap.ContainsKey(methodInfo->scope)) 127 | { 128 | relatedAssembly = _instance._scopeMap[methodInfo->scope]; 129 | } 130 | else 131 | { 132 | _instance.EntryCount--; 133 | return o(thisPtr, corJitInfo, methodInfo, flags, 134 | nativeEntry, nativeSizeOfCode); 135 | } 136 | 137 | var ra = new RawArguments 138 | { 139 | ThisPtr = thisPtr, 140 | CorJitInfo = corJitInfo, 141 | MethodInfo = safeMethodInfo, 142 | Flags = flags, 143 | NativeEntry = nativeEntry, 144 | NativeSizeOfCode = nativeSizeOfCode 145 | }; 146 | 147 | byte[] il = new byte[methodInfo->ilCodeSize]; 148 | Marshal.Copy((IntPtr)methodInfo->ilCode, il, 0, il.Length); 149 | 150 | // Extra sections contains the exception handlers. 151 | byte[] extraSections = new byte[0]; 152 | if (methodInfo->EHCount > 0) 153 | { 154 | byte* extraSectionsPtr = methodInfo->ilCode + methodInfo->ilCodeSize; 155 | extraSections = TryReadExtraSections(extraSectionsPtr); 156 | } 157 | 158 | _instance.OnCompileMethod -= OnCompileEventResetMethod; 159 | _instance.OnCompileMethod += OnCompileEventResetMethod; 160 | _instance.OnCompileMethod(ra, relatedAssembly, token, ref il, ref extraSections); 161 | _instance._compileMethodResetEvent.WaitOne(); 162 | 163 | // Assume something has changed. 164 | thisPtr = ra.ThisPtr; 165 | corJitInfo = ra.CorJitInfo; 166 | methodInfo = (Data.CorMethodInfo*)ra.MethodInfo.ToPointer(); 167 | flags = ra.Flags; 168 | nativeEntry = ra.NativeEntry; 169 | nativeSizeOfCode = ra.NativeSizeOfCode; 170 | 171 | // IL code and extra sections 172 | var ilCodeHandle = Marshal.AllocHGlobal(il.Length + extraSections.Length * 2); 173 | Marshal.Copy(il, 0, ilCodeHandle, il.Length); 174 | Data.VirtualProtect((IntPtr)methodInfo->ilCode, (uint)IntPtr.Size, Data.Protection.PAGE_READWRITE, 175 | out uint prevProt); 176 | methodInfo->ilCode = (byte*)ilCodeHandle.ToPointer(); 177 | methodInfo->ilCodeSize = (uint)il.Length; 178 | Marshal.Copy(extraSections, 0, (IntPtr)(methodInfo->ilCode + methodInfo->ilCodeSize), 179 | extraSections.Length); 180 | 181 | #if NET4 182 | if (methodInfo->EHCount > 0 || extraSections.Length > 2) 183 | { 184 | exceptionHandlerHook = new EHInfoHook(corJitInfo, methodInfo->ftn, il, extraSections); 185 | } 186 | #endif 187 | } 188 | 189 | var res = o(thisPtr, corJitInfo, methodInfo, flags, 190 | nativeEntry, nativeSizeOfCode); 191 | #if NET4 192 | exceptionHandlerHook?.Dispose(); 193 | #endif 194 | _instance.EntryCount--; 195 | return res; 196 | } 197 | 198 | [MethodImpl(MethodImplOptions.NoInlining)] 199 | private static int HookedCompileMethod64(IntPtr thisPtr, [In] IntPtr corJitInfo, 200 | [In] Data.CorMethodInfo64* methodInfo, Data.CorJitFlag flags, 201 | [Out] IntPtr nativeEntry, [Out] IntPtr nativeSizeOfCode) 202 | { 203 | // THIS IS THE 64 BIT COMPILE METHOD. 204 | if (thisPtr == IntPtr.Zero) return 0; 205 | 206 | var o = _instance._hookHelper.Hook.OriginalCompileMethod64; 207 | 208 | _instance.EntryCount++; 209 | if (_instance.EntryCount > 1) 210 | { 211 | _instance.EntryCount--; 212 | return o(thisPtr, corJitInfo, methodInfo, flags, 213 | nativeEntry, nativeSizeOfCode); 214 | } 215 | 216 | 217 | var safeMethodInfo = new IntPtr((int*)methodInfo); 218 | #if NET4 219 | EHInfoHook exceptionHandlerHook = null; 220 | #endif 221 | 222 | #if NET4 223 | var functionPtr = Marshal.ReadIntPtr(Marshal.ReadIntPtr(corJitInfo), IntPtr.Size * GET_METHOD_DEF_FROM_METHOD_SLOT_INDEX); 224 | var getMethodDef = (Data.GetMethodDefFromMethodDel)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(Data.GetMethodDefFromMethodDel)); 225 | var token = getMethodDef(corJitInfo, methodInfo->ftn); 226 | #else 227 | var token = (uint)(0x06000000 | *(ushort*)methodInfo->ftn); 228 | #endif 229 | 230 | lock (_jitCompileMethodLock) 231 | { 232 | 233 | Assembly relatedAssembly = null; 234 | if (!_instance._scopeMap.ContainsKey(methodInfo->scope)) 235 | _instance._scopeMap = AppDomain.CurrentDomain.GetScopeMap(); 236 | if (_instance._scopeMap.ContainsKey(methodInfo->scope)) 237 | { 238 | relatedAssembly = _instance._scopeMap[methodInfo->scope]; 239 | } 240 | else 241 | { 242 | _instance.EntryCount--; 243 | return o(thisPtr, corJitInfo, methodInfo, flags, 244 | nativeEntry, nativeSizeOfCode); 245 | } 246 | 247 | var ra = new RawArguments 248 | { 249 | ThisPtr = thisPtr, 250 | CorJitInfo = corJitInfo, 251 | MethodInfo = safeMethodInfo, 252 | Flags = flags, 253 | NativeEntry = nativeEntry, 254 | NativeSizeOfCode = nativeSizeOfCode 255 | }; 256 | 257 | byte[] il = new byte[methodInfo->ilCodeSize]; 258 | Marshal.Copy((IntPtr)methodInfo->ilCode, il, 0, il.Length); 259 | 260 | // Extra sections contains the exception handlers. 261 | byte[] extraSections = new byte[0]; 262 | if (methodInfo->EHCount > 0) 263 | { 264 | byte* extraSectionsPtr = methodInfo->ilCode + methodInfo->ilCodeSize; 265 | extraSections = TryReadExtraSections(extraSectionsPtr); 266 | } 267 | 268 | _instance.OnCompileMethod -= OnCompileEventResetMethod; 269 | _instance.OnCompileMethod += OnCompileEventResetMethod; 270 | _instance.OnCompileMethod(ra, relatedAssembly, token, ref il, ref extraSections); 271 | _instance._compileMethodResetEvent.WaitOne(); 272 | 273 | // Assume something has changed. 274 | thisPtr = ra.ThisPtr; 275 | corJitInfo = ra.CorJitInfo; 276 | methodInfo = (Data.CorMethodInfo64*)ra.MethodInfo.ToPointer(); 277 | flags = ra.Flags; 278 | nativeEntry = ra.NativeEntry; 279 | nativeSizeOfCode = ra.NativeSizeOfCode; 280 | 281 | // IL code and extra sections 282 | var ilCodeHandle = Marshal.AllocHGlobal(il.Length); 283 | Marshal.Copy(il, 0, ilCodeHandle, il.Length); 284 | Data.VirtualProtect((IntPtr)methodInfo->ilCode, (uint)IntPtr.Size, Data.Protection.PAGE_READWRITE, 285 | out uint prevProt); 286 | methodInfo->ilCode = (byte*)ilCodeHandle.ToPointer(); 287 | methodInfo->ilCodeSize = (uint)il.Length; 288 | 289 | #if NET4 290 | if (methodInfo->EHCount > 0 || extraSections.Length > 2) 291 | { 292 | exceptionHandlerHook = new EHInfoHook(corJitInfo, methodInfo->ftn, il, extraSections); 293 | } 294 | #endif 295 | } 296 | 297 | var res = o(thisPtr, corJitInfo, methodInfo, flags, 298 | nativeEntry, nativeSizeOfCode); 299 | 300 | #if NET4 301 | exceptionHandlerHook?.Dispose(); 302 | #endif 303 | _instance.EntryCount--; 304 | return res; 305 | } 306 | 307 | #region Other CorJitInfo Hooks 308 | #if NET4 309 | private class EHInfoHook 310 | { 311 | private const int SLOT_COUNT = 173; // Difference of 7 for slot counts in core. 312 | private const int SLOT_INDEX = 8; 313 | 314 | public readonly IntPtr CorJitInfoPtr; 315 | public readonly IntPtr* NewVfTable; 316 | public readonly IntPtr* OldVfTable; 317 | 318 | public readonly Data.CorInfoEhClause[] ExceptionHandlers; 319 | 320 | public readonly Data.GetEHInfoDel OriginalMethod; 321 | public readonly Data.GetEHInfoDel NewMethod; 322 | public readonly IntPtr MethodFtn; 323 | 324 | [MethodImpl(MethodImplOptions.NoInlining)] 325 | public EHInfoHook(IntPtr corJitInfo, IntPtr ftn, byte[] ilBytes, byte[] ehBytes) 326 | 327 | { 328 | MethodFtn = ftn; 329 | CorJitInfoPtr = corJitInfo; 330 | ExceptionHandlers = ehBytes.GetExceptionClauses(ilBytes.GetInstructions()).ToArray(); 331 | 332 | OldVfTable = (IntPtr*)Marshal.ReadIntPtr(corJitInfo); 333 | 334 | // Slot number, the amount of function pointers in the vftable. 335 | // Original from ConfuserEx: 158 336 | NewVfTable = (IntPtr*)Marshal.AllocHGlobal(IntPtr.Size * SLOT_COUNT); 337 | for (int i = 0; i < SLOT_COUNT; i++) 338 | { 339 | NewVfTable[i] = OldVfTable[i]; 340 | } 341 | 342 | NewMethod = Hook; 343 | OriginalMethod = 344 | (Data.GetEHInfoDel)Marshal.GetDelegateForFunctionPointer(OldVfTable[SLOT_INDEX], typeof(Data.GetEHInfoDel)); 345 | 346 | NewVfTable[SLOT_INDEX] = Marshal.GetFunctionPointerForDelegate(NewMethod); 347 | 348 | Marshal.WriteIntPtr(corJitInfo, 0, (IntPtr)NewVfTable); 349 | } 350 | 351 | [MethodImpl(MethodImplOptions.NoInlining)] 352 | public void Dispose() 353 | { 354 | Marshal.FreeHGlobal((IntPtr)NewVfTable); 355 | Marshal.WriteIntPtr(CorJitInfoPtr, 0, (IntPtr)OldVfTable); 356 | } 357 | 358 | [MethodImpl(MethodImplOptions.NoInlining)] 359 | private void Hook(IntPtr ptr, IntPtr ftn, uint ehNumber, Data.CorInfoEhClause* clause) 360 | { 361 | if (ftn == MethodFtn) 362 | { 363 | // Clause is OUT, get the exception handler bytes and get the correct clause. 364 | *clause = ExceptionHandlers[ehNumber]; 365 | } 366 | else 367 | { 368 | // The old getEHInfo method. 369 | OriginalMethod(ptr, ftn, ehNumber, clause); 370 | } 371 | } 372 | } 373 | #endif 374 | #endregion 375 | 376 | 377 | #region Extra Section Reader 378 | 379 | // Credits to 0xd4d 380 | // https://github.com/0xd4d/de4dot/blob/master/de4dot.mdecrypt/DynamicMethodsDecrypter.cs 381 | private static byte[] TryReadExtraSections(byte* p) 382 | { 383 | try 384 | { 385 | p = Align(p, 4); 386 | byte* startPos = p; 387 | p = ParseSection(p); 388 | var size = (int)(p - startPos); 389 | byte[] sections = new byte[size]; 390 | Marshal.Copy((IntPtr)startPos, sections, 0, sections.Length); 391 | return sections; 392 | } 393 | catch (Exception ex) 394 | { 395 | return new byte[0]; 396 | } 397 | } 398 | 399 | private static byte* Align(byte* p, int alignment) 400 | { 401 | return (byte*)new IntPtr((long)((ulong)(p + alignment - 1) & ~(ulong)(alignment - 1))); 402 | } 403 | 404 | private static byte* ParseSection(byte* p) 405 | { 406 | byte flags; 407 | do 408 | { 409 | p = Align(p, 4); 410 | 411 | flags = *p++; 412 | if ((flags & 1) == 0) 413 | throw new ApplicationException("Not an exception section"); 414 | if ((flags & 0x3E) != 0) 415 | throw new ApplicationException("Invalid bits set"); 416 | 417 | if ((flags & 0x40) != 0) 418 | { 419 | p--; 420 | int num = (int)(*(uint*)p >> 8) / 24; 421 | p += 4 + num * 24; 422 | } 423 | else 424 | { 425 | int num = *p++ / 12; 426 | p += 2 + num * 12; 427 | } 428 | } while ((flags & 0x80) != 0); 429 | 430 | return p; 431 | } 432 | 433 | #endregion 434 | } 435 | } -------------------------------------------------------------------------------- /TinyJitHook.Core/Models/IHookHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using TinyJitHook.Core.SJITHook; 4 | 5 | namespace TinyJitHook.Core.Models 6 | { 7 | /// 8 | /// A hook helper that provides wrapping to an underlying JIT hook. 9 | /// 10 | public interface IHookHelper 11 | { 12 | /// 13 | /// The underlying JIT hook. 14 | /// 15 | IJitHook Hook { get; } 16 | 17 | /// 18 | /// The loaded assembly. 19 | /// 20 | Assembly LoadedAssembly { get; } 21 | 22 | /// 23 | /// The module scope of the manifest module. 24 | /// 25 | IntPtr ModuleScope { get; } 26 | 27 | /// 28 | /// Apply the hook. 29 | /// 30 | /// Whether the hook was applied. 31 | bool Apply(); 32 | 33 | /// 34 | /// Remove the hook. 35 | /// 36 | /// Whether the hook was removed. 37 | bool Remove(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TinyJitHook.Core/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using TinyJitHook.Core.Extensions; 4 | using TinyJitHook.Core.SJITHook; 5 | 6 | namespace TinyJitHook.Core 7 | { 8 | public class Program 9 | { 10 | public static void Main(string[] args) 11 | { 12 | Assembly asm = Assembly.LoadFrom(@"TinyJitHook.Core.TestApp_x64.dll"); 13 | MainJitHook hook = new MainJitHook(asm, IntPtr.Size == 8); 14 | 15 | hook.OnCompileMethod += ChangeExample; 16 | 17 | hook.Hook(); 18 | 19 | asm.EntryPoint.Invoke(null, new object[] { new[] { "" } }); 20 | 21 | hook.Unhook(); 22 | 23 | Console.WriteLine("DONE"); 24 | Console.ReadKey(); 25 | } 26 | 27 | private static unsafe void ChangeExample(MainJitHook.RawArguments args, Assembly relatedAssembly, uint methodToken, ref byte[] ilBytes, ref byte[] ehBytes) 28 | { 29 | try 30 | { 31 | var methodBase = relatedAssembly.ManifestModule.ResolveMethod((int)methodToken); 32 | Data.CorMethodInfo* rawMethodInfo = (Data.CorMethodInfo*)args.MethodInfo.ToPointer(); 33 | 34 | var insts = ilBytes.GetInstructions(); 35 | 36 | Logger.LogInfo(typeof(Program), $"---------------------------------------"); 37 | Logger.LogSuccess(typeof(Program), $"{methodBase.DeclaringType?.FullName}.{methodBase.Name}"); 38 | Logger.LogSuccess(typeof(Program), $"Inst Count: {insts.Count}"); 39 | Logger.LogSuccess(typeof(Program), $"Exception Handler Count: {rawMethodInfo->EHCount}"); 40 | 41 | if (rawMethodInfo->EHCount > 0) 42 | { 43 | var ehs = ehBytes.GetExceptionHandlers(insts); 44 | for (var i = 0; i < ehs.Count; i++) 45 | { 46 | var eh = ehs[i]; 47 | Logger.LogWarn(typeof(Program), $"Exception Handler {i + 1}:"); 48 | Logger.LogWarn(typeof(Program), $" Type: {eh.HandlerType}"); 49 | Logger.LogWarn(typeof(Program), $" TryStart: {eh.TryStart}"); 50 | Logger.LogWarn(typeof(Program), $" TryEnd: {eh.TryEnd}"); 51 | Logger.LogWarn(typeof(Program), $" CatchTypeToken: {eh.CatchTypeToken}"); 52 | } 53 | } 54 | 55 | foreach (var inst in insts) 56 | { 57 | Logger.Log(typeof(Program), $"{inst}"); 58 | } 59 | } 60 | catch (Exception ex) 61 | { 62 | // RIP 63 | } 64 | } 65 | // Token: 0x0400000B RID: 11 66 | public static string bytes = "AChNAAAKAAAWKE4AAAoAcwsAAAYoTwAACgAA3hEKAHK5AgBwBihQAAAKAADeACo="; //"AChNAAAKAAAWKE4AAAoAcwsAAAYoTwAACgAA3hEKAHK5AgBwBihQAAAKAADeACo="; 67 | 68 | // Token: 0x0400000C RID: 12 69 | public static string bytes2 = "ARAAAAAABwAWHQARHwAAAQ=="; // "ARAAAAAABwAWHQARHwAAAQ=="; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /TinyJitHook.Core/SJITHook/ClrjitAddrProvider.cs: -------------------------------------------------------------------------------- 1 | /*The MIT License (MIT) 2 | 3 | Copyright (c) 2014 UbbeLoL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.*/ 22 | 23 | using System; 24 | using System.Runtime.InteropServices; 25 | 26 | namespace TinyJitHook.Core.SJITHook 27 | { 28 | /// 29 | /// The JIT address provider for .NET 4.0 CLR. 30 | /// 31 | public sealed class ClrjitAddrProvider : VTableAddrProvider 32 | { 33 | [DllImport("Clrjit.dll", CallingConvention = CallingConvention.StdCall, PreserveSig = true)] 34 | private static extern IntPtr getJit(); 35 | 36 | /// 37 | /// Get the Virtual Table Address of the JIT. 38 | /// 39 | public override IntPtr VTableAddr 40 | { 41 | get 42 | { 43 | IntPtr pVTable = getJit(); 44 | if (pVTable == IntPtr.Zero) 45 | throw new Exception("Could not retrieve address for getJit"); 46 | 47 | return pVTable; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /TinyJitHook.Core/SJITHook/IJitHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace TinyJitHook.Core.SJITHook 5 | { 6 | /// 7 | /// JIT hook interface to abstract it out a bit. 8 | /// 9 | public interface IJitHook 10 | { 11 | /// 12 | /// The VTable Address that the address provider retrieved. 13 | /// 14 | IntPtr VTableAddress { get; } 15 | 16 | /// 17 | /// The original compile method (x86) if the JIT hook is for 32 bit (otherwise null). 18 | /// 19 | Data.CompileMethodDel OriginalCompileMethod32 { [MethodImpl(MethodImplOptions.NoInlining)] get; } 20 | /// 21 | /// The original compile method (x64) if the JIT hook is for 64 bit (otherwise null) 22 | /// 23 | Data.CompileMethodDel64 OriginalCompileMethod64 { [MethodImpl(MethodImplOptions.NoInlining)] get; } 24 | 25 | /// 26 | /// Hook the compileMethod function and redirect to the supplied callback. 27 | /// 28 | /// Whether it was successfully hooked. 29 | bool Hook(); 30 | /// 31 | /// Unhook the compileMethod function and stop redirection to the callback. 32 | /// 33 | /// Whether it was successfully removed. 34 | bool UnHook(); 35 | 36 | /// 37 | /// Prepare internal methods so that we do not run into loops. 38 | /// 39 | void PrepareInternalMethods(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /TinyJitHook.Core/SJITHook/JITHook.cs: -------------------------------------------------------------------------------- 1 | /*The MIT License (MIT) 2 | 3 | Copyright (c) 2014 UbbeLoL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.*/ 22 | 23 | using System; 24 | using System.Runtime.CompilerServices; 25 | using System.Runtime.InteropServices; 26 | 27 | namespace TinyJitHook.Core.SJITHook 28 | { 29 | /// 30 | /// A 32 bit JIT hook that works on a to redirect the pointer to compileMethod. 31 | /// 32 | /// A vtable address provider. 33 | public unsafe class JITHook : IJitHook where T : VTableAddrProvider 34 | { 35 | 36 | private readonly byte[] _delegateTrampolineCode = { 37 | 0x68, 0x00, 0x00, 0x00, 0x00, 38 | 0xc3 39 | }; 40 | 41 | /// 42 | public IntPtr VTableAddress 43 | { 44 | 45 | [MethodImpl(MethodImplOptions.NoInlining)] 46 | get => pVTable; 47 | } 48 | 49 | private readonly T _addrProvider; 50 | 51 | /// 52 | /// The original compile method. 53 | /// 54 | public Data.CompileMethodDel OriginalCompileMethod32 { [MethodImpl(MethodImplOptions.NoInlining)]get; private set; } 55 | /// 56 | /// Completely unused, will return null. Do not use. 57 | /// 58 | public Data.CompileMethodDel64 OriginalCompileMethod64 { [MethodImpl(MethodImplOptions.NoInlining)]get; private set; } 59 | 60 | /// 61 | /// The callback to use instead of the original compile method. 62 | /// 63 | public Data.CompileMethodDel HookedCompileMethod { [MethodImpl(MethodImplOptions.NoInlining)]get; set; } 64 | 65 | 66 | private IntPtr pVTable; 67 | private IntPtr pCompileMethod; 68 | private uint old; 69 | 70 | /// 71 | /// Create a new 32 bit JIT hook (does not hook). Will change memory flags to PAGE_EXECUTE_READWRITE for the compileMethod region (4 or 8 bytes). 72 | /// 73 | /// The callback to replace the original. 74 | public JITHook(Data.CompileMethodDel hookedCompileMethod) 75 | { 76 | _addrProvider = Activator.CreateInstance(); 77 | HookedCompileMethod = hookedCompileMethod; 78 | 79 | pVTable = _addrProvider.VTableAddr; 80 | pCompileMethod = Marshal.ReadIntPtr(pVTable); 81 | 82 | if ( 83 | !Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 84 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 85 | throw new Exception("Cannot change memory protection flags."); 86 | 87 | OriginalCompileMethod32 = 88 | (Data.CompileMethodDel) 89 | Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(pCompileMethod), typeof(Data.CompileMethodDel)); 90 | } 91 | 92 | /// 93 | /// Hook the compileMethod function and redirect to the supplied callback. 94 | /// 95 | /// Whether it was successfully hooked. 96 | public bool Hook() 97 | { 98 | PrepareInternalMethods(); 99 | 100 | IntPtr fPtr = Marshal.GetFunctionPointerForDelegate(HookedCompileMethod); 101 | PreLoad(fPtr); 102 | 103 | Marshal.WriteIntPtr(pCompileMethod, fPtr); 104 | 105 | return Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 106 | (Data.Protection)old, out old); 107 | } 108 | 109 | /// 110 | public void PrepareInternalMethods() 111 | { 112 | // We don't want any infinite loops :-) 113 | RuntimeHelpers.PrepareDelegate(HookedCompileMethod); 114 | RuntimeHelpers.PrepareDelegate(OriginalCompileMethod32); 115 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_OriginalCompileMethod64").MethodHandle, new[] { typeof(T).TypeHandle }); 116 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_OriginalCompileMethod32").MethodHandle, new[] { typeof(T).TypeHandle }); 117 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_HookedCompileMethod").MethodHandle, new[] { typeof(T).TypeHandle }); 118 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("UnHook").MethodHandle, new[] { typeof(T).TypeHandle }); 119 | } 120 | 121 | /// 122 | /// Unhook the compileMethod function and stop redirection to the callback. 123 | /// 124 | /// Whether it was successfully removed. 125 | public bool UnHook() 126 | { 127 | if ( 128 | !Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 129 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 130 | return false; 131 | 132 | Marshal.WriteIntPtr(pCompileMethod, Marshal.GetFunctionPointerForDelegate(OriginalCompileMethod32)); 133 | 134 | return Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 135 | (Data.Protection)old, out old); 136 | } 137 | 138 | 139 | private void PreLoad(IntPtr fPtr) 140 | { 141 | // Trampoline to reverse pinvoke to test... 142 | var tPtr = AllocateTrampoline(fPtr); 143 | var t = (Data.CompileMethodDel64)Marshal.GetDelegateForFunctionPointer( 144 | tPtr, typeof(Data.CompileMethodDel64)); 145 | 146 | t(IntPtr.Zero, IntPtr.Zero, (Data.CorMethodInfo64*)IntPtr.Zero.ToPointer(), default(Data.CorJitFlag), 147 | IntPtr.Zero, IntPtr.Zero); 148 | // Free it. 149 | Marshal.FreeHGlobal(tPtr); 150 | } 151 | 152 | private IntPtr AllocateTrampoline(IntPtr ptr) 153 | { 154 | var jmpNative = Marshal.AllocHGlobal(_delegateTrampolineCode.Length); 155 | if (!Data.VirtualProtect(jmpNative, (uint)_delegateTrampolineCode.Length, 156 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 157 | return IntPtr.Zero; 158 | Marshal.Copy(_delegateTrampolineCode, 0, jmpNative, _delegateTrampolineCode.Length); 159 | Marshal.WriteIntPtr(jmpNative, 1, ptr); 160 | return jmpNative; 161 | } 162 | } 163 | } -------------------------------------------------------------------------------- /TinyJitHook.Core/SJITHook/JITHook64.cs: -------------------------------------------------------------------------------- 1 | /*The MIT License (MIT) 2 | 3 | Copyright (c) 2014 UbbeLoL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.*/ 22 | 23 | using System; 24 | using System.Runtime.CompilerServices; 25 | using System.Runtime.InteropServices; 26 | 27 | namespace TinyJitHook.Core.SJITHook 28 | { 29 | /// 30 | /// A 64 bit JIT hook that works on a to redirect the pointer to compileMethod. 31 | /// 32 | /// A vtable address provider. 33 | public unsafe class JITHook64 : IJitHook where T : VTableAddrProvider 34 | { 35 | // Credits: http://xoofx.com/blog/2018/04/12/writing-managed-jit-in-csharp-with-coreclr/ 36 | private readonly byte[] _delegateTrampolineCode = { 37 | // mov rax, 0000000000000000h ;Pointer address to _overrideCompileMethodPtr 38 | 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 39 | // jmp rax 40 | 0xFF, 0xE0 41 | }; 42 | 43 | /// 44 | public IntPtr VTableAddress 45 | { 46 | 47 | [MethodImpl(MethodImplOptions.NoInlining)] 48 | get => pVTable; 49 | } 50 | 51 | private readonly T _addrProvider; 52 | /// 53 | /// The original compile method. 54 | /// 55 | 56 | public Data.CompileMethodDel64 OriginalCompileMethod64 { [MethodImpl(MethodImplOptions.NoInlining)] get; private set; } 57 | 58 | /// 59 | /// Completely unused, will return null. Do not use. 60 | /// 61 | public Data.CompileMethodDel OriginalCompileMethod32 { [MethodImpl(MethodImplOptions.NoInlining)] get; private set; } 62 | 63 | /// 64 | /// The callback to use instead of the original compile method. 65 | /// 66 | public Data.CompileMethodDel64 HookedCompileMethod { [MethodImpl(MethodImplOptions.NoInlining)] get; set; } 67 | 68 | private IntPtr pVTable; 69 | private IntPtr pCompileMethod; 70 | private uint old; 71 | 72 | 73 | /// 74 | /// Create a new 64 bit JIT hook (does not hook). Will change memory flags to PAGE_EXECUTE_READWRITE for the compileMethod region (4 or 8 bytes). 75 | /// 76 | /// The callback to replace the original. 77 | public JITHook64(Data.CompileMethodDel64 hookedCompileMethod) 78 | { 79 | _addrProvider = Activator.CreateInstance(); 80 | HookedCompileMethod = hookedCompileMethod; 81 | 82 | pVTable = _addrProvider.VTableAddr; 83 | pCompileMethod = Marshal.ReadIntPtr(pVTable, 0); 84 | 85 | if ( 86 | !Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 87 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 88 | throw new Exception("Cannot change memory protection flags."); 89 | 90 | OriginalCompileMethod64 = 91 | (Data.CompileMethodDel64) 92 | Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(pCompileMethod), typeof(Data.CompileMethodDel64)); 93 | 94 | Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 95 | (Data.Protection)old, out old); 96 | } 97 | 98 | /// 99 | /// Hook the compileMethod function and redirect to the supplied callback. 100 | /// 101 | /// Whether it was successfully hooked. 102 | public bool Hook() 103 | { 104 | PrepareInternalMethods(); 105 | 106 | if ( 107 | !Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 108 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 109 | throw new Exception("Cannot change memory protection flags."); 110 | 111 | IntPtr fPtr = Marshal.GetFunctionPointerForDelegate(HookedCompileMethod); 112 | PreLoad(fPtr); 113 | 114 | Marshal.WriteIntPtr(pCompileMethod, fPtr); 115 | 116 | ; return Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 117 | (Data.Protection)old, out old); 118 | } 119 | 120 | /// 121 | public void PrepareInternalMethods() 122 | { 123 | // We don't want any infinite loops :-) 124 | RuntimeHelpers.PrepareDelegate(HookedCompileMethod); 125 | RuntimeHelpers.PrepareDelegate(OriginalCompileMethod64); 126 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_OriginalCompileMethod64").MethodHandle, new[] { typeof(T).TypeHandle }); 127 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_OriginalCompileMethod32").MethodHandle, new[] { typeof(T).TypeHandle }); 128 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_HookedCompileMethod").MethodHandle, new[] { typeof(T).TypeHandle }); 129 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("UnHook").MethodHandle, new[] { typeof(T).TypeHandle }); 130 | } 131 | 132 | /// 133 | /// Unhook the compileMethod function and stop redirection to the callback. 134 | /// 135 | /// Whether it was successfully removed. 136 | public bool UnHook() 137 | { 138 | IntPtr pVTable = _addrProvider.VTableAddr; 139 | IntPtr pCompileMethod = Marshal.ReadIntPtr(pVTable); 140 | uint old; 141 | 142 | if ( 143 | !Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 144 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 145 | return false; 146 | 147 | Marshal.WriteIntPtr(pCompileMethod, Marshal.GetFunctionPointerForDelegate(OriginalCompileMethod64)); 148 | 149 | return Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 150 | (Data.Protection)old, out old); 151 | } 152 | 153 | private void PreLoad(IntPtr fPtr) 154 | { 155 | // Trampoline to reverse pinvoke to test... 156 | var tPtr = AllocateTrampoline(fPtr); 157 | var t = (Data.CompileMethodDel64)Marshal.GetDelegateForFunctionPointer( 158 | tPtr, typeof(Data.CompileMethodDel64)); 159 | 160 | t(IntPtr.Zero, IntPtr.Zero, (Data.CorMethodInfo64*)IntPtr.Zero.ToPointer(), default(Data.CorJitFlag), 161 | IntPtr.Zero, IntPtr.Zero); 162 | // Free it. 163 | Marshal.FreeHGlobal(tPtr); 164 | } 165 | 166 | private IntPtr AllocateTrampoline(IntPtr ptr) 167 | { 168 | var jmpNative = Marshal.AllocHGlobal(_delegateTrampolineCode.Length); 169 | if (!Data.VirtualProtect(jmpNative, (uint)_delegateTrampolineCode.Length, 170 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 171 | return IntPtr.Zero; 172 | Marshal.Copy(_delegateTrampolineCode, 0, jmpNative, _delegateTrampolineCode.Length); 173 | Marshal.WriteIntPtr(jmpNative, 2, ptr); 174 | return jmpNative; 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /TinyJitHook.Core/SJITHook/VTableAddrProvider.cs: -------------------------------------------------------------------------------- 1 | /*The MIT License (MIT) 2 | 3 | Copyright (c) 2014 UbbeLoL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.*/ 22 | 23 | using System; 24 | 25 | namespace TinyJitHook.Core.SJITHook 26 | { 27 | /// 28 | /// The VTableAddressProvider class will provide an address for the vtable for a particular CLR version. 29 | /// 30 | public abstract class VTableAddrProvider 31 | { 32 | internal delegate IntPtr GetJit(); 33 | /// 34 | /// Get the Virtual Table Address of the JIT. 35 | /// 36 | public abstract IntPtr VTableAddr { get; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /TinyJitHook.Core/TinyJitHook.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | AnyCPU;x64;x86 7 | TinyJitHook.Core.Program 8 | 9 | 10 | 11 | true 12 | TRACE;NET4 13 | ..\DebugCore\ 14 | 15 | 16 | 17 | true 18 | TRACE;NET4 19 | ..\DebugCore\ 20 | 21 | 22 | 23 | ..\DebugCore\ 24 | true 25 | TRACE;NET4 26 | false 27 | 28 | 29 | 30 | ..\DebugCore\ 31 | true 32 | TRACE;NET4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /TinyJitHook.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29503.13 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinyJitHook", "TinyJitHook\TinyJitHook.csproj", "{30E40BD9-A0EE-451C-B24D-90B0B2485384}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TinyJitHook.Core", "TinyJitHook.Core\TinyJitHook.Core.csproj", "{D8731662-0C29-4A67-B7ED-FE0E7E4305F5}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TinyJitHook.Core.TestApp", "TinyJitHook.Core.TestApp\TinyJitHook.Core.TestApp.csproj", "{707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|x64 = Debug|x64 16 | Debug|x86 = Debug|x86 17 | Release|Any CPU = Release|Any CPU 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Debug|x64.ActiveCfg = Debug|x64 25 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Debug|x64.Build.0 = Debug|x64 26 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Debug|x86.ActiveCfg = Debug|x86 27 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Debug|x86.Build.0 = Debug|x86 28 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Release|x64.ActiveCfg = Release|x64 31 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Release|x64.Build.0 = Release|x64 32 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Release|x86.ActiveCfg = Release|x86 33 | {30E40BD9-A0EE-451C-B24D-90B0B2485384}.Release|x86.Build.0 = Release|x86 34 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Debug|x64.ActiveCfg = Debug|x64 37 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Debug|x64.Build.0 = Debug|x64 38 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Debug|x86.ActiveCfg = Debug|x86 39 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Debug|x86.Build.0 = Debug|x86 40 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Release|x64.ActiveCfg = Release|Any CPU 43 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Release|x64.Build.0 = Release|Any CPU 44 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Release|x86.ActiveCfg = Release|Any CPU 45 | {D8731662-0C29-4A67-B7ED-FE0E7E4305F5}.Release|x86.Build.0 = Release|Any CPU 46 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Debug|x64.ActiveCfg = Debug|x64 49 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Debug|x64.Build.0 = Debug|x64 50 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Debug|x86.ActiveCfg = Debug|x86 51 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Debug|x86.Build.0 = Debug|x86 52 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Release|x64.ActiveCfg = Release|Any CPU 55 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Release|x64.Build.0 = Release|Any CPU 56 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Release|x86.ActiveCfg = Release|Any CPU 57 | {707DCBB3-BFB0-4A74-B187-5C6F7C64B48D}.Release|x86.Build.0 = Release|Any CPU 58 | EndGlobalSection 59 | GlobalSection(SolutionProperties) = preSolution 60 | HideSolutionNode = FALSE 61 | EndGlobalSection 62 | GlobalSection(ExtensibilityGlobals) = postSolution 63 | SolutionGuid = {45481323-3522-43AC-A618-01FEC5880749} 64 | EndGlobalSection 65 | EndGlobal 66 | -------------------------------------------------------------------------------- /TinyJitHook/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /TinyJitHook/ExceptionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using TinyJitHook.Extensions; 7 | 8 | namespace TinyJitHook 9 | { 10 | // Credits: https://github.com/0xd4d/dnlib 11 | // License: https://github.com/0xd4d/dnlib/blob/master/LICENSE.txt 12 | 13 | /// 14 | /// A CIL method exception handler 15 | /// 16 | public sealed class ExceptionHandler 17 | { 18 | /// 19 | /// First instruction of try block 20 | /// 21 | public Instruction TryStart; 22 | public uint TryStartRaw; 23 | 24 | /// 25 | /// One instruction past the end of try block or null if it ends at the end 26 | /// of the method. 27 | /// 28 | public Instruction TryEnd; 29 | public uint TryEndRaw; 30 | 31 | /// 32 | /// Start of filter handler or null if none. The end of filter handler is 33 | /// always . 34 | /// 35 | public Instruction FilterStart; 36 | public uint FilterStartRaw; 37 | 38 | /// 39 | /// First instruction of try handler block 40 | /// 41 | public Instruction HandlerStart; 42 | public uint HandlerStartRaw; 43 | 44 | /// 45 | /// One instruction past the end of try handler block or null if it ends at the end 46 | /// of the method. 47 | /// 48 | public Instruction HandlerEnd; 49 | public uint HandlerEndRaw; 50 | 51 | /// 52 | /// Type of exception handler clause 53 | /// 54 | public ExceptionHandlerType HandlerType; 55 | 56 | public uint CatchTypeToken; 57 | 58 | /// 59 | /// Default constructor 60 | /// 61 | public ExceptionHandler() 62 | { 63 | } 64 | 65 | public static Instruction GetInstruction(uint off, List body) 66 | { 67 | return body.FirstOrDefault(inst => inst.Offset == off); 68 | } 69 | 70 | public static uint GetOffset(Instruction inst, List body, List recalcBody) 71 | { 72 | // TODO: Index of does not work with the recalculated body, the instruction is different 73 | // Custom comparator!? 74 | int index1 = body.IndexOf(inst); 75 | if (index1 < 0) 76 | { 77 | return uint.MaxValue; 78 | } 79 | 80 | int index2 = recalcBody.IndexOf(inst); 81 | if (index2 < 0) 82 | { 83 | // Hack to fix this, rely on the first index. 84 | index2 = index1; 85 | } 86 | 87 | return body[index1].Offset == 0 ? (uint) recalcBody[index2].Offset : (uint) body[index1].Offset; 88 | } 89 | 90 | public void ReadBig(BinaryReader r, List body) 91 | { 92 | HandlerType = (ExceptionHandlerType)r.ReadUInt32(); 93 | uint offset = r.ReadUInt32(); 94 | TryStart = GetInstruction(offset, body); 95 | TryStartRaw = offset; 96 | 97 | var off2 = offset + r.ReadUInt32(); 98 | TryEnd = GetInstruction(off2, body); 99 | TryEndRaw = off2; 100 | 101 | offset = r.ReadUInt32(); 102 | HandlerStart = GetInstruction(offset, body); 103 | HandlerStartRaw = offset; 104 | 105 | var off3 = offset + r.ReadUInt32(); 106 | HandlerEnd = GetInstruction(off3, body); 107 | HandlerEndRaw = off3; 108 | 109 | if (HandlerType == ExceptionHandlerType.Catch) 110 | { 111 | CatchTypeToken = r.ReadUInt32(); 112 | } 113 | else if (HandlerType == ExceptionHandlerType.Filter) 114 | { 115 | var off4 = r.ReadUInt32(); 116 | FilterStart = GetInstruction(off4, body); 117 | FilterStartRaw = off4; 118 | } 119 | else 120 | { 121 | r.ReadUInt32(); 122 | } 123 | } 124 | 125 | public void ReadSmall(BinaryReader r, List body) 126 | { 127 | HandlerType = (ExceptionHandlerType) r.ReadUInt16(); 128 | uint offset = r.ReadUInt16(); 129 | TryStart = GetInstruction(offset, body); 130 | TryStartRaw = offset; 131 | 132 | var off2 = offset + r.ReadByte(); 133 | TryEnd = GetInstruction(off2, body); 134 | TryEndRaw = off2; 135 | 136 | offset = r.ReadUInt16(); 137 | HandlerStart = GetInstruction(offset, body); 138 | HandlerStartRaw = offset; 139 | 140 | var off3 = offset + r.ReadByte(); 141 | HandlerEnd = GetInstruction(off3, body); 142 | HandlerEndRaw = off3; 143 | 144 | if (HandlerType == ExceptionHandlerType.Catch) 145 | { 146 | CatchTypeToken = r.ReadUInt32(); 147 | } 148 | else if (HandlerType == ExceptionHandlerType.Filter) 149 | { 150 | var off4 = r.ReadUInt32(); 151 | FilterStart = GetInstruction(off4, body); 152 | FilterStartRaw = off4; 153 | } 154 | else 155 | { 156 | r.ReadUInt32(); 157 | } 158 | } 159 | 160 | public void WriteBig(BinaryWriter w, List body, List rBody) 161 | { 162 | w.Write((uint)HandlerType); 163 | 164 | uint offs1 = GetOffset(TryStart, body, rBody); 165 | uint offs2 = GetOffset(TryEnd, body, rBody); 166 | if (offs2 <= offs1) 167 | throw new Exception("Exception handler: TryEnd <= TryStart"); 168 | w.Write(offs1); 169 | w.Write(offs2 - offs1); 170 | 171 | offs1 = GetOffset(HandlerStart, body, rBody); 172 | offs2 = GetOffset(HandlerEnd, body, rBody); 173 | if (offs2 <= offs1) 174 | throw new Exception("Exception handler: HandlerEnd <= HandlerStart"); 175 | w.Write(offs1); 176 | w.Write(offs2 - offs1); 177 | 178 | if (HandlerType == ExceptionHandlerType.Catch) 179 | w.Write(CatchTypeToken); 180 | else if (HandlerType == ExceptionHandlerType.Filter) 181 | w.Write(GetOffset(FilterStart, body, rBody)); 182 | else 183 | w.Write(0); 184 | } 185 | public void WriteSmall(BinaryWriter w, List body, List rBody) 186 | { 187 | w.Write((ushort)HandlerType); 188 | 189 | uint offs1 = GetOffset(TryStart, body, rBody); 190 | uint offs2 = GetOffset(TryEnd, body, rBody); 191 | if (offs2 <= offs1) 192 | throw new Exception("Exception handler: TryEnd <= TryStart"); 193 | w.Write((ushort)offs1); 194 | w.Write((byte)(offs2 - offs1)); 195 | 196 | offs1 = GetOffset(HandlerStart, body, rBody); 197 | offs2 = GetOffset(HandlerEnd, body, rBody); 198 | if (offs2 <= offs1) 199 | throw new Exception("Exception handler: HandlerEnd <= HandlerStart"); 200 | w.Write((ushort)offs1); 201 | w.Write((byte)(offs2 - offs1)); 202 | 203 | if (HandlerType == ExceptionHandlerType.Catch) 204 | w.Write(CatchTypeToken); 205 | else if (HandlerType == ExceptionHandlerType.Filter) 206 | w.Write(GetOffset(FilterStart, body, rBody)); 207 | else 208 | w.Write(0); 209 | } 210 | } 211 | 212 | /// 213 | /// Type of exception handler. See CorHdr.h/CorExceptionFlag 214 | /// 215 | [Flags] 216 | public enum ExceptionHandlerType 217 | { 218 | /// 219 | Catch = 0x0000, 220 | /// 221 | Filter = 0x0001, 222 | /// 223 | Finally = 0x0002, 224 | /// 225 | Fault = 0x0004, 226 | /// 227 | Duplicated = 0x0008, 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /TinyJitHook/Extensions/ExceptionHandlerByteHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using TinyJitHook.SJITHook; 6 | 7 | namespace TinyJitHook.Extensions 8 | { 9 | public static class ExceptionHandlerByteHelper 10 | { 11 | public static List GetExceptionClauses(this byte[] data, 12 | List relatedMethodBody) 13 | { 14 | List ehList = data.GetExceptionHandlers(relatedMethodBody); 15 | List exceptionHandlers = new List(); 16 | foreach (var eh in ehList) 17 | { 18 | exceptionHandlers.Add(new Data.CorInfoEhClause 19 | { 20 | Flags = (uint)eh.HandlerType, 21 | TryOffset = eh.TryStartRaw, 22 | TryLength = eh.TryEndRaw - eh.TryStartRaw, 23 | HandlerOffset = eh.HandlerStartRaw, 24 | HandlerLength = eh.HandlerEndRaw - eh.HandlerStartRaw, 25 | ClassTokenOrFilterOffset = eh.CatchTypeToken 26 | }); 27 | } 28 | 29 | return exceptionHandlers; 30 | } 31 | public static List GetExceptionHandlers( 32 | this byte[] data, List relatedMethodBody) 33 | { 34 | var ret = new List(); 35 | 36 | var ehReader = new BinaryReader(new MemoryStream(data)); 37 | 38 | byte b = ehReader.ReadByte(); 39 | if ((b & 0x3F) != 1) 40 | return new List(); // Not exception handler clauses 41 | 42 | ret.AddRange((b & 0x40) != 0 43 | ? ReadBigExceptionHandlers(ehReader, relatedMethodBody) 44 | : ReadSmallExceptionHandlers(ehReader, relatedMethodBody)); 45 | 46 | return ret; 47 | } 48 | 49 | public static byte[] GetExceptionHandlerBytes(this List exceptionHandlers, 50 | List relatedMethodBody) 51 | { 52 | // Recalculate offsets 53 | byte[] tmpBody = relatedMethodBody.GetInstructionBytes(); 54 | List rBody = tmpBody.GetInstructions().ToList(); 55 | 56 | byte[] extraSections; 57 | if (NeedBigExceptionClauses(exceptionHandlers, relatedMethodBody, rBody)) 58 | extraSections = WriteBigExceptionClauses(exceptionHandlers, relatedMethodBody, rBody); 59 | else 60 | extraSections = WriteSmallExceptionClauses(exceptionHandlers, relatedMethodBody, rBody); 61 | return extraSections; 62 | 63 | } 64 | 65 | private static byte[] WriteBigExceptionClauses(List exceptionHandlers, 66 | List relatedMethodBody, List rBody) 67 | { 68 | const int maxExceptionHandlers = (0x00FFFFFF - 4) / 24; 69 | int numExceptionHandlers = exceptionHandlers.Count; 70 | if (numExceptionHandlers > maxExceptionHandlers) throw new Exception("Too many exception handlers"); 71 | 72 | byte[] data = new byte[numExceptionHandlers * 24 + 4]; 73 | using (var ms = new MemoryStream(data)) 74 | using (var writer = new BinaryWriter(ms)) 75 | { 76 | writer.Write((((uint) numExceptionHandlers * 24 + 4) << 8) | 0x41); 77 | for (var i = 0; i < numExceptionHandlers; i++) 78 | { 79 | var eh = exceptionHandlers[i]; 80 | eh.WriteBig(writer, relatedMethodBody, rBody); 81 | } 82 | 83 | if (writer.BaseStream.Position != data.Length) 84 | throw new InvalidOperationException(); 85 | } 86 | 87 | return data; 88 | } 89 | 90 | private static byte[] WriteSmallExceptionClauses(List exceptionHandlers, 91 | List relatedMethodBody, List rBody) 92 | { 93 | const int maxExceptionHandlers = (0xFF - 4) / 12; 94 | int numExceptionHandlers = exceptionHandlers.Count; 95 | if (numExceptionHandlers > maxExceptionHandlers) throw new Exception("Too many exception handlers"); 96 | 97 | byte[] data = new byte[numExceptionHandlers * 12 + 4]; 98 | using (var ms = new MemoryStream(data)) 99 | using (var writer = new BinaryWriter(ms)) 100 | { 101 | writer.Write((((uint) numExceptionHandlers * 12 + 4) << 8) | 1); 102 | for (var i = 0; i < numExceptionHandlers; i++) 103 | { 104 | var eh = exceptionHandlers[i]; 105 | eh.WriteSmall(writer, relatedMethodBody, rBody); 106 | } 107 | 108 | if (writer.BaseStream.Position != data.Length) 109 | throw new InvalidOperationException(); 110 | } 111 | 112 | return data; 113 | } 114 | 115 | private static List ReadBigExceptionHandlers( 116 | BinaryReader ehReader, List body) 117 | { 118 | List ret = new List(); 119 | ehReader.BaseStream.Position--; 120 | int num = (ushort) ((ehReader.ReadUInt32() >> 8) / 24); 121 | for (var i = 0; i < num; i++) 122 | { 123 | var eh = new ExceptionHandler(); 124 | eh.ReadBig(ehReader, body); 125 | ret.Add(eh); 126 | } 127 | 128 | return ret; 129 | } 130 | 131 | private static List ReadSmallExceptionHandlers( 132 | BinaryReader ehReader, List body) 133 | { 134 | List ret = new List(); 135 | int num = (ushort) ((uint) ehReader.ReadByte() / 12); 136 | ehReader.BaseStream.Position += 2; 137 | for (var i = 0; i < num; i++) 138 | { 139 | var eh = new ExceptionHandler(); 140 | eh.ReadSmall(ehReader, body); 141 | ret.Add(eh); 142 | } 143 | 144 | return ret; 145 | } 146 | 147 | private static bool NeedBigExceptionClauses(List exceptionHandlers, List body, 148 | List rBody) 149 | { 150 | // Size must fit in a byte, and since one small exception record is 12 bytes 151 | // and header is 4 bytes: x*12+4 <= 255 ==> x <= 20 152 | if (exceptionHandlers.Count > 20) 153 | return true; 154 | 155 | foreach (var eh in exceptionHandlers) 156 | { 157 | if (!FitsInSmallExceptionClause(eh.TryStart, eh.TryEnd, body, rBody)) 158 | return true; 159 | if (!FitsInSmallExceptionClause(eh.HandlerStart, eh.HandlerEnd, body, rBody)) 160 | return true; 161 | } 162 | 163 | return false; 164 | } 165 | 166 | private static bool FitsInSmallExceptionClause(Instruction start, Instruction end, List body, 167 | List rBody) 168 | { 169 | uint offs1 = ExceptionHandler.GetOffset(start, body, rBody); 170 | uint offs2 = ExceptionHandler.GetOffset(end, body, rBody); 171 | if (offs2 < offs1) 172 | return false; 173 | return offs1 <= ushort.MaxValue && offs2 - offs1 <= byte.MaxValue; 174 | } 175 | } 176 | } -------------------------------------------------------------------------------- /TinyJitHook/Extensions/InstructionByteHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | 4 | namespace TinyJitHook.Extensions 5 | { 6 | public static class InstructionByteHelper 7 | { 8 | public static List GetInstructions(this byte[] bytes) 9 | { 10 | var ret = new List(); 11 | using (MemoryStream ms = new MemoryStream(bytes)) 12 | using (BinaryReader r = new BinaryReader(ms)) 13 | { 14 | while (r.BaseStream.Position < bytes.Length) 15 | { 16 | Instruction instruction = new Instruction { Offset = (int)r.BaseStream.Position }; 17 | 18 | short code = r.ReadByte(); 19 | if (code == 0xfe) 20 | { 21 | code = (short)(r.ReadByte() | 0xfe00); 22 | } 23 | 24 | instruction.OpCode = code.GetOpCode(); 25 | instruction.Read(r); 26 | 27 | ret.Add(instruction); 28 | } 29 | } 30 | 31 | return ret; 32 | } 33 | 34 | public static byte[] GetInstructionBytes(this List instructions) 35 | { 36 | using (MemoryStream ms = new MemoryStream()) 37 | using (BinaryWriter w = new BinaryWriter(ms)) 38 | { 39 | foreach (var inst in instructions) 40 | { 41 | if (inst.OpCode.Size == 1) 42 | { 43 | w.Write((byte)inst.OpCode.Value); 44 | } 45 | else 46 | { 47 | w.Write(inst.OpCode.Value); 48 | } 49 | 50 | inst.Write(w); 51 | } 52 | 53 | return ms.ToArray(); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /TinyJitHook/Extensions/JitHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Runtime.CompilerServices; 5 | using TinyJitHook.SJITHook; 6 | 7 | namespace TinyJitHook.Extensions 8 | { 9 | public static class JitHelper 10 | { 11 | public static void PrepareAssemblies(this AppDomain domain) 12 | { 13 | foreach (var asm in domain.GetAssemblies()) 14 | { 15 | asm.PrepareMethods(); 16 | } 17 | } 18 | public static void PrepareMethods(this Assembly asm) 19 | { 20 | foreach (Type t in asm.GetTypes()) 21 | { 22 | if (t.ContainsGenericParameters || t.IsGenericType) 23 | continue; 24 | 25 | foreach (MethodInfo mi in t.GetMethods()) 26 | { 27 | if (mi.IsAbstract || mi.IsGenericMethod || mi.IsGenericMethodDefinition) 28 | continue; 29 | if (mi.DeclaringType != null && 30 | (mi.DeclaringType.IsGenericType || mi.DeclaringType.IsGenericTypeDefinition)) 31 | continue; 32 | RuntimeHelpers.PrepareMethod(mi.MethodHandle); 33 | } 34 | 35 | foreach (ConstructorInfo ci in t.GetConstructors()) 36 | { 37 | if (ci.IsAbstract) 38 | continue; 39 | RuntimeHelpers.PrepareMethod(ci.MethodHandle); 40 | } 41 | } 42 | } 43 | public static void PrepareOriginalCompileGetter(this IJitHook hook, bool is64Bit) 44 | { 45 | // Preload the original method property getter 46 | Type hookType = hook.GetType(); 47 | Type[] genParams = hookType.GetGenericArguments(); 48 | RuntimeTypeHandle[] genRTs = new RuntimeTypeHandle[genParams.Length]; 49 | 50 | for (int i = 0; i < genParams.Length; i++) 51 | { 52 | genRTs[i] = genParams[i].TypeHandle; 53 | } 54 | 55 | RuntimeHelpers.PrepareMethod( 56 | hookType.GetMethod($"get_OriginalCompileMethod{(is64Bit ? "64" : "32")}").MethodHandle, genRTs); 57 | } 58 | public static IntPtr[] GetAssemblyScopes(this AppDomain domain) 59 | { 60 | Assembly[] asms = domain.GetAssemblies(); 61 | var moduleScopes = new IntPtr[asms.Length]; 62 | for (int i = 0; i < asms.Length; i++) 63 | { 64 | // The invoking assembly is generally at index 1. 65 | moduleScopes[i] = asms[i].ManifestModule.GetScope(); 66 | } 67 | 68 | return moduleScopes; 69 | } 70 | public static Dictionary GetScopeMap(this AppDomain domain) 71 | { 72 | var moduleScopes = new Dictionary(); 73 | foreach (var assembly in domain.GetAssemblies()) 74 | { 75 | moduleScopes.Add(assembly.ManifestModule.GetScope(), assembly); 76 | } 77 | 78 | return moduleScopes; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /TinyJitHook/Extensions/OpCodeShortHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection.Emit; 3 | 4 | namespace TinyJitHook.Extensions 5 | { 6 | public static class OpCodeShortHelper 7 | { 8 | private static readonly Dictionary _opCodes = new Dictionary(); 9 | 10 | static OpCodeShortHelper() 11 | { 12 | Initialize(); 13 | } 14 | 15 | 16 | public static OpCode GetOpCode(this short value) 17 | { 18 | return _opCodes[value]; 19 | } 20 | 21 | 22 | private static void Initialize() 23 | { 24 | foreach (var fieldInfo in typeof(OpCodes).GetFields()) 25 | { 26 | var opCode = (OpCode) fieldInfo.GetValue(null); 27 | 28 | _opCodes.Add(opCode.Value, opCode); 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /TinyJitHook/Extensions/PathHelper.cs: -------------------------------------------------------------------------------- 1 | namespace TinyJitHook.Extensions 2 | { 3 | public static class PathHelper 4 | { 5 | /// 6 | /// Get a custom branded file path, defaults to _dumped 7 | /// 8 | /// The filepath to brand. 9 | /// The brand to give it (goes after the file name, but before the extension. 10 | /// A full file path with the custom brand (C:\something_brandhere.exe). 11 | public static string GetFilePath(this string filePath, string brand = "_dumped") 12 | { 13 | string outFile = filePath; 14 | int index = outFile.LastIndexOf('.'); 15 | if (index != -1) 16 | { 17 | outFile = outFile.Insert(index, brand); 18 | } 19 | return outFile; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TinyJitHook/Extensions/ReflectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace TinyJitHook.Extensions 5 | { 6 | /// 7 | /// A general class to help with many things related to reflection. 8 | /// 9 | public static class ReflectionHelper 10 | { 11 | /// 12 | /// Get a module scope. 13 | /// 14 | /// The module to get the scope of. 15 | /// The module's scope. 16 | public static IntPtr GetScope(this Module mod) 17 | { 18 | var obj = mod.ModuleHandle.GetFieldValue("m_ptr"); 19 | if (obj is IntPtr) 20 | { 21 | return (IntPtr)obj; 22 | } 23 | if (obj.GetType().ToString() == "System.Reflection.RuntimeModule") 24 | { 25 | return obj.GetFieldValue("m_pData"); 26 | } 27 | 28 | throw new Exception("Cannot get scope of module."); 29 | } 30 | /// 31 | /// Get a field value from a field using reflection. 32 | /// 33 | /// Type of the field (the return type). 34 | /// The object to get the field from. 35 | /// The field name of the field to retrieve value of. 36 | /// The value of the field casted to T. 37 | public static T GetFieldValue(this object obj, string fieldName) 38 | { 39 | if (obj == null) 40 | throw new ArgumentNullException("obj"); 41 | var rtType = obj.GetType(); 42 | // var fields = rtType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 43 | var field = rtType.GetField(fieldName, BindingFlags.Public | 44 | BindingFlags.NonPublic | 45 | BindingFlags.Instance); 46 | 47 | if (field == null) 48 | throw new ArgumentException("fieldName", "No such field was found."); 49 | 50 | if (!typeof(T).IsAssignableFrom(field.FieldType)) 51 | throw new InvalidOperationException("Field type and requested type are not compatible."); 52 | 53 | return (T)field.GetValue(obj); 54 | } 55 | /// 56 | /// Get a field value from a field of a specific type using reflection. 57 | /// 58 | /// Type of the field (the return type). 59 | /// The object. 60 | /// The field name of the field to retrieve value of. 61 | /// The type to use to get the field from. 62 | /// The value of the field casted to T 63 | public static T GetFieldValue(this object obj, string fieldName, Type theType) 64 | { 65 | if (obj == null) 66 | throw new ArgumentNullException("obj"); 67 | 68 | // var fields = theType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 69 | var field = theType.GetField(fieldName, BindingFlags.Public | 70 | BindingFlags.NonPublic | 71 | BindingFlags.Instance); 72 | 73 | if (field == null) 74 | throw new ArgumentException("fieldName", "No such field was found."); 75 | 76 | if (!typeof(T).IsAssignableFrom(field.FieldType)) 77 | throw new InvalidOperationException("Field type and requested type are not compatible."); 78 | 79 | return (T)field.GetValue(obj); 80 | } 81 | /// 82 | /// Set a field's value using reflection. 83 | /// 84 | /// The type instance that holds the field to set the value of. 85 | /// The field name. 86 | /// The data to set the field to, must be the field type. 87 | public static void SetFieldValue(this object obj, string fieldName, object data) 88 | { 89 | if (obj == null) 90 | throw new ArgumentNullException("obj"); 91 | 92 | var field = obj.GetType().GetField(fieldName, BindingFlags.Public | 93 | BindingFlags.NonPublic | 94 | BindingFlags.Instance); 95 | 96 | if (field == null) 97 | throw new ArgumentException("fieldName", "No such field was found."); 98 | 99 | field.SetValue(obj, data); 100 | } 101 | 102 | /// 103 | /// Get a method in a type with the flags specified, will work with internal and private types. 104 | /// 105 | /// The full name (including namespace) of the type. 106 | /// The method name to retrieve. 107 | /// The binding flags to use to find the method on the type. 108 | /// Whether or not to immediately return when found. 109 | /// The method with the name supplied. 110 | public static MethodInfo GetMethod(this string type, string method, BindingFlags flags, bool breakOnFind = true) 111 | { 112 | MethodInfo found = null; 113 | foreach (MethodInfo mInfo in typeof(Assembly).Assembly.GetType(type) 114 | .GetMethods(flags)) 115 | { 116 | if (mInfo.Name == method) 117 | { 118 | found = mInfo; 119 | if (breakOnFind) 120 | { 121 | break; 122 | } 123 | } 124 | } 125 | return found; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /TinyJitHook/HookHelpers/HookHelper32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using TinyJitHook.Extensions; 4 | using TinyJitHook.Models; 5 | using TinyJitHook.SJITHook; 6 | 7 | namespace TinyJitHook.HookHelpers 8 | { 9 | /// 10 | /// A 32 bit hook helper that wraps around SJITHook's implementation. 11 | /// 12 | public class HookHelper32 : IHookHelper 13 | { 14 | /// 15 | /// The original CompileMethod. 16 | /// 17 | public Data.CompileMethodDel Original; 18 | 19 | /// 20 | /// The loaded assembly. 21 | /// 22 | public Assembly LoadedAssembly { get; private set; } 23 | 24 | /// 25 | /// The underlying JIT hook used. 26 | /// 27 | public IJitHook Hook { get; private set; } 28 | 29 | /// 30 | /// The loaded module's scope. 31 | /// 32 | public IntPtr ModuleScope { get; private set; } 33 | 34 | /// 35 | /// Initialize a new 32 bit hook for JIT on the supplied assembly with the callback CompileMethod. 36 | /// Will determine the runtime version (and JIT hook to use). 37 | /// 38 | /// The assembly to wrap 39 | /// The CompileMethod to call instead of the original. 40 | public HookHelper32(Assembly asm, Data.CompileMethodDel hookedCompileMethod32) 41 | { 42 | 43 | LoadedAssembly = asm; 44 | ModuleScope = LoadedAssembly.ManifestModule.GetScope(); 45 | 46 | if (LoadedAssembly.ImageRuntimeVersion == "v2.0.50727") 47 | { 48 | // .NET 2.0-3.5 49 | Hook = new JITHook(hookedCompileMethod32); 50 | } 51 | else 52 | { 53 | // .NET 4.0+ 54 | Hook = new JITHook(hookedCompileMethod32); 55 | } 56 | 57 | Original = Hook.OriginalCompileMethod32; 58 | } 59 | 60 | /// 61 | /// Apply the JIT hook and load the assembly (does invoke the module static constructor!). 62 | /// 63 | /// Whether or not the JIT hook was applied. 64 | public bool Apply() 65 | { 66 | return Hook.Hook(); 67 | } 68 | 69 | /// 70 | /// Remove the JIT hook and restore the original. 71 | /// 72 | /// Whether or not the JIT hook was successfully unhooked. 73 | public bool Remove() 74 | { 75 | return Hook.UnHook(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /TinyJitHook/HookHelpers/HookHelper64.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using TinyJitHook.Extensions; 4 | using TinyJitHook.Models; 5 | using TinyJitHook.SJITHook; 6 | 7 | namespace TinyJitHook.HookHelpers 8 | { 9 | /// 10 | /// A 64 bit hook helper that wraps around SJITHook's implementation. 11 | /// 12 | public class HookHelper64 : IHookHelper 13 | { 14 | /// 15 | /// The original CompileMethod. 16 | /// 17 | public Data.CompileMethodDel64 Original; 18 | 19 | /// 20 | /// The loaded assembly. 21 | /// 22 | public Assembly LoadedAssembly { get; private set; } 23 | 24 | /// 25 | /// The underlying JIT hook used. 26 | /// 27 | public IJitHook Hook { get; private set; } 28 | 29 | /// 30 | /// The loaded module's scope. 31 | /// 32 | public IntPtr ModuleScope { get; private set; } 33 | 34 | /// 35 | /// Initialize a new 64 bit hook for JIT on the supplied assembly with the callback CompileMethod. 36 | /// Will determine the runtime version (and JIT hook to use). 37 | /// 38 | /// The assembly to wrap. 39 | /// The CompileMethod to call instead of the original. 40 | public HookHelper64(Assembly asm, Data.CompileMethodDel64 hookedCompileMethod64) 41 | { 42 | LoadedAssembly = asm; 43 | ModuleScope = LoadedAssembly.ManifestModule.GetScope(); 44 | 45 | if (LoadedAssembly.ImageRuntimeVersion == "v2.0.50727") 46 | { 47 | // .NET 2.0-3.5 48 | Hook = new JITHook64(hookedCompileMethod64); 49 | } 50 | else 51 | { 52 | // .NET 4.0+ 53 | Hook = new JITHook64(hookedCompileMethod64); 54 | } 55 | 56 | Original = Hook.OriginalCompileMethod64; 57 | } 58 | 59 | /// 60 | /// Apply the JIT hook and load the assembly (does invoke the module static constructor!). 61 | /// 62 | /// Whether or not the JIT hook was applied. 63 | public bool Apply() 64 | { 65 | return Hook.Hook(); 66 | } 67 | 68 | /// 69 | /// Remove the JIT hook and restore the original. 70 | /// 71 | /// Whether or not the JIT hook was successfully unhooked. 72 | public bool Remove() 73 | { 74 | return Hook.UnHook(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /TinyJitHook/InjectionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Reflection.Emit; 7 | using System.Security.Cryptography.X509Certificates; 8 | using System.Text; 9 | using TinyJitHook.Extensions; 10 | using TinyJitHook.SJITHook; 11 | 12 | namespace TinyJitHook 13 | { 14 | public static class InjectionTest 15 | { 16 | // Required to invoke static constructor. 17 | public static BinaryReader r; 18 | 19 | static InjectionTest() 20 | { 21 | using (Stream resFilestream = typeof(InjectionTest).Assembly.GetManifestResourceStream("Name")) 22 | { 23 | byte[] ba = new byte[resFilestream.Length]; 24 | resFilestream.Read(ba, 0, ba.Length); 25 | r = new BinaryReader(new MemoryStream(ba)); 26 | } 27 | Start(); 28 | } 29 | 30 | private static void Start() 31 | { 32 | MainJitHook hook = new MainJitHook(typeof(InjectionTest).Assembly, IntPtr.Size == 8); 33 | hook.OnCompileMethod += CompileMethod; 34 | hook.Hook(); 35 | } 36 | 37 | private static unsafe void CompileMethod(MainJitHook.RawArguments args, Assembly relatedAssembly, uint methodToken, ref byte[] ilBytes, ref byte[] ehBytes) 38 | { 39 | 40 | //if (relatedAssembly != null) 41 | //{ 42 | // Console.WriteLine($"0x{methodToken:x8} - {relatedAssembly.FullName}"); 43 | //} 44 | //else 45 | //{ 46 | // Console.WriteLine($"0x{methodToken:x8}"); 47 | //} 48 | if (relatedAssembly != typeof(InjectionTest).Assembly) 49 | { 50 | return; 51 | } 52 | 53 | try 54 | { 55 | var methodBase = relatedAssembly.ManifestModule.ResolveMethod((int) methodToken); 56 | //var nameOfMethod = methodBase.Name; 57 | //if (nameOfMethod == "GetHash") 58 | //{ 59 | // nameOfMethod += "test"; 60 | //} 61 | var insts = ilBytes.GetInstructions(); 62 | int index = -1; 63 | 64 | if (insts[0].OpCode == OpCodes.Ldc_I4) 65 | index = (int) insts[0].Data; 66 | else if (insts[0].OpCode == OpCodes.Ldc_I4_S) 67 | index = (int) (byte) insts[0].Data; 68 | else if (insts[0].OpCode == OpCodes.Ldc_I4_0) 69 | index = 0; 70 | else if (insts[0].OpCode == OpCodes.Ldc_I4_1) 71 | index = 1; 72 | else if (insts[0].OpCode == OpCodes.Ldc_I4_2) 73 | index = 2; 74 | else if (insts[0].OpCode == OpCodes.Ldc_I4_3) 75 | index = 3; 76 | else if (insts[0].OpCode == OpCodes.Ldc_I4_4) 77 | index = 4; 78 | else if (insts[0].OpCode == OpCodes.Ldc_I4_5) 79 | index = 5; 80 | else if (insts[0].OpCode == OpCodes.Ldc_I4_6) 81 | index = 6; 82 | else if (insts[0].OpCode == OpCodes.Ldc_I4_7) 83 | index = 7; 84 | else if (insts[0].OpCode == OpCodes.Ldc_I4_8) 85 | index = 8; 86 | 87 | if (index == -1) 88 | return; 89 | r.BaseStream.Position = index; 90 | 91 | int ilByteCount = r.ReadInt32(); 92 | byte[] newIL = r.ReadBytes(ilByteCount); 93 | int extraCount = r.ReadInt32(); 94 | byte[] newEH = r.ReadBytes(extraCount); 95 | 96 | ilBytes = newIL; 97 | ehBytes = newEH; 98 | } 99 | catch (Exception) 100 | { 101 | 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /TinyJitHook/Instruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection.Emit; 4 | 5 | namespace TinyJitHook 6 | { 7 | public sealed class Instruction 8 | { 9 | public int Offset { get; set; } 10 | 11 | 12 | public OpCode OpCode { get; set; } 13 | 14 | public object Data { get; set; } 15 | 16 | public static Instruction Create(OpCode opcode, object data = null) 17 | { 18 | var inst = new Instruction 19 | { 20 | OpCode = opcode, 21 | Data = data 22 | }; 23 | return inst; 24 | } 25 | 26 | public void Read(BinaryReader r) 27 | { 28 | switch (OpCode.OperandType) 29 | { 30 | case OperandType.InlineBrTarget: 31 | Data = r.ReadInt32(); 32 | break; 33 | 34 | case OperandType.InlineField: 35 | Data = r.ReadInt32(); 36 | break; 37 | 38 | case OperandType.InlineI: 39 | Data = r.ReadInt32(); 40 | break; 41 | 42 | case OperandType.InlineI8: 43 | Data = r.ReadInt64(); 44 | break; 45 | 46 | case OperandType.InlineMethod: 47 | Data = r.ReadInt32(); 48 | break; 49 | 50 | case OperandType.InlineNone: 51 | break; 52 | 53 | case OperandType.InlineR: 54 | Data = r.ReadDouble(); 55 | break; 56 | 57 | case OperandType.InlineSig: 58 | Data = r.ReadInt32(); 59 | break; 60 | 61 | case OperandType.InlineString: 62 | Data = r.ReadInt32(); 63 | break; 64 | 65 | case OperandType.InlineSwitch: 66 | int count = r.ReadInt32() + 1; 67 | Data = r.ReadBytes(4 * count); 68 | break; 69 | 70 | case OperandType.InlineTok: 71 | Data = r.ReadInt32(); 72 | break; 73 | 74 | case OperandType.InlineType: 75 | Data = r.ReadInt32(); 76 | break; 77 | 78 | case OperandType.InlineVar: 79 | Data = r.ReadBytes(2); 80 | break; 81 | 82 | case OperandType.ShortInlineBrTarget: 83 | Data = r.ReadByte(); 84 | break; 85 | 86 | case OperandType.ShortInlineI: 87 | Data = r.ReadByte(); 88 | break; 89 | 90 | case OperandType.ShortInlineR: 91 | Data = r.ReadSingle(); 92 | break; 93 | 94 | case OperandType.ShortInlineVar: 95 | Data = r.ReadByte(); 96 | break; 97 | 98 | default: 99 | throw new NotImplementedException(); 100 | } 101 | 102 | } 103 | 104 | public void Write(BinaryWriter w) 105 | { 106 | switch (OpCode.OperandType) 107 | { 108 | case OperandType.InlineBrTarget: 109 | w.Write((int) Data); 110 | break; 111 | 112 | case OperandType.InlineField: 113 | w.Write((int) Data); 114 | break; 115 | 116 | case OperandType.InlineI: 117 | w.Write((int) Data); 118 | break; 119 | 120 | case OperandType.InlineI8: 121 | w.Write((long) Data); 122 | break; 123 | 124 | case OperandType.InlineMethod: 125 | w.Write((int) Data); 126 | break; 127 | 128 | case OperandType.InlineNone: 129 | break; 130 | 131 | case OperandType.InlineR: 132 | w.Write((double) Data); 133 | break; 134 | 135 | case OperandType.InlineSig: 136 | w.Write((int) Data); 137 | break; 138 | 139 | case OperandType.InlineString: 140 | w.Write((int) Data); 141 | break; 142 | 143 | case OperandType.InlineSwitch: 144 | w.Write(((byte[]) Data).Length / 4 - 1); 145 | w.Write((byte[]) Data); 146 | break; 147 | 148 | case OperandType.InlineTok: 149 | w.Write((int) Data); 150 | break; 151 | 152 | case OperandType.InlineType: 153 | w.Write((int) Data); 154 | break; 155 | 156 | case OperandType.InlineVar: 157 | w.Write((byte[]) Data); 158 | break; 159 | 160 | case OperandType.ShortInlineBrTarget: 161 | w.Write((byte) Data); 162 | break; 163 | 164 | case OperandType.ShortInlineI: 165 | w.Write((sbyte) Data); 166 | break; 167 | 168 | case OperandType.ShortInlineR: 169 | w.Write((float) Data); 170 | break; 171 | 172 | case OperandType.ShortInlineVar: 173 | w.Write((byte) Data); 174 | break; 175 | 176 | default: 177 | throw new NotImplementedException(); 178 | } 179 | 180 | } 181 | 182 | public override string ToString() 183 | { 184 | return $"{Offset:X4}: {OpCode} - {Data}"; 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /TinyJitHook/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TinyJitHook 4 | { 5 | public static class Logger 6 | { 7 | /// 8 | /// Whether or not to suppress info messages. 9 | /// 10 | public static bool SuppressInfo { get; set; } 11 | /// 12 | /// Whether or not to suppress warning messages. 13 | /// 14 | public static bool SuppressWarn { get; set; } 15 | /// 16 | /// Whether or not to suppress all logging. 17 | /// 18 | public static bool SuppressLogging { get; set; } 19 | 20 | /// 21 | /// Generic log to console with no color. 22 | /// 23 | /// The type (will print name of type) that is calling. 24 | /// The message to log. 25 | public static void Log(Type t, string msg) 26 | { 27 | if (SuppressLogging) 28 | { 29 | return; 30 | } 31 | string data = $"[{(t == null ? "GLOBAL" : t.Name)}] {msg}"; 32 | Console.WriteLine(data); 33 | } 34 | /// 35 | /// Log a success message () to console. 36 | /// 37 | /// The type (will print name of type) that is calling. 38 | /// The message to log. 39 | public static void LogSuccess(Type t, string msg) 40 | { 41 | Console.ForegroundColor = ConsoleColor.Green; 42 | Log(t, msg); 43 | Console.ResetColor(); 44 | } 45 | /// 46 | /// Log a warning message () to console. 47 | /// 48 | /// The type (will print name of type) that is calling. 49 | /// The message to log. 50 | public static void LogWarn(Type t, string msg) 51 | { 52 | if (SuppressWarn) 53 | { 54 | return; 55 | } 56 | Console.ForegroundColor = ConsoleColor.Yellow; 57 | Log(t, msg); 58 | Console.ResetColor(); 59 | } 60 | /// 61 | /// Log an info message () to console. 62 | /// 63 | /// The type (will print name of type) that is calling. 64 | /// The message to log. 65 | public static void LogInfo(Type t, string msg) 66 | { 67 | if (SuppressInfo) 68 | { 69 | return; 70 | } 71 | Console.ForegroundColor = ConsoleColor.Cyan; 72 | Log(t, msg); 73 | Console.ResetColor(); 74 | } 75 | /// 76 | /// Log an error message () to console. 77 | /// 78 | /// The type (will print name of type) that is calling. 79 | /// The message to log. 80 | public static void LogError(Type t, string msg) 81 | { 82 | Console.ForegroundColor = ConsoleColor.Red; 83 | Log(t, msg); 84 | Console.ResetColor(); 85 | } 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /TinyJitHook/MainJitHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | using System.Threading; 7 | using TinyJitHook.Extensions; 8 | using TinyJitHook.HookHelpers; 9 | using TinyJitHook.Models; 10 | using TinyJitHook.SJITHook; 11 | 12 | namespace TinyJitHook 13 | { 14 | public unsafe class MainJitHook 15 | { 16 | private const int GET_METHOD_DEF_FROM_METHOD_SLOT_INDEX = 105; 17 | private static MainJitHook _instance; 18 | private readonly IHookHelper _hookHelper; 19 | 20 | public readonly bool Is64Bit; 21 | public int EntryCount; 22 | 23 | private Dictionary _scopeMap; 24 | 25 | public delegate void ActionDelegate(RawArguments args, Assembly relatedAssembly, 26 | uint methodToken, ref byte[] ilBytes, ref byte[] ehBytes); 27 | 28 | #region Events 29 | 30 | private readonly AutoResetEvent _compileMethodResetEvent; 31 | private static object _jitCompileMethodLock = new object(); 32 | public event ActionDelegate OnCompileMethod; 33 | 34 | #endregion 35 | 36 | public class RawArguments 37 | { 38 | public IntPtr ThisPtr; 39 | public IntPtr CorJitInfo; 40 | public IntPtr MethodInfo; 41 | public Data.CorJitFlag Flags; 42 | 43 | public IntPtr NativeEntry; 44 | public IntPtr NativeSizeOfCode; 45 | } 46 | 47 | public MainJitHook(Assembly asm, bool is64Bit) 48 | { 49 | Is64Bit = is64Bit; 50 | EntryCount = 0; 51 | 52 | _instance = this; 53 | _compileMethodResetEvent = new AutoResetEvent(false); 54 | 55 | if (is64Bit) 56 | _hookHelper = new HookHelper64(asm, HookedCompileMethod64); 57 | else 58 | _hookHelper = new HookHelper32(asm, HookedCompileMethod32); 59 | 60 | _hookHelper.Hook.PrepareOriginalCompileGetter(Is64Bit); 61 | //Assembly.GetExecutingAssembly().PrepareMethods(); 62 | 63 | //AppDomain.CurrentDomain.PrepareAssemblies(); 64 | 65 | _scopeMap = AppDomain.CurrentDomain.GetScopeMap(); 66 | 67 | } 68 | 69 | public void Hook() 70 | { 71 | if (!_hookHelper.Apply()) throw new Exception("Hook could not be applied."); 72 | } 73 | 74 | public void Unhook() 75 | { 76 | if (!_hookHelper.Remove()) throw new Exception("Hook could not be removed."); 77 | } 78 | 79 | [MethodImpl(MethodImplOptions.NoInlining)] 80 | private static void OnCompileEventResetMethod(RawArguments args, Assembly assembly, uint methodToken, 81 | ref byte[] bytes, 82 | ref byte[] ehBytes) 83 | { 84 | _instance._compileMethodResetEvent.Set(); 85 | } 86 | 87 | [MethodImpl(MethodImplOptions.NoInlining)] 88 | private static int HookedCompileMethod32(IntPtr thisPtr, [In] IntPtr corJitInfo, 89 | [In] Data.CorMethodInfo* methodInfo, Data.CorJitFlag flags, 90 | [Out] IntPtr nativeEntry, [Out] IntPtr nativeSizeOfCode) 91 | { 92 | // THIS IS THE 32 BIT COMPILE METHOD. 93 | 94 | if (thisPtr == IntPtr.Zero) return 0; 95 | 96 | var o = _instance._hookHelper.Hook.OriginalCompileMethod32; 97 | 98 | _instance.EntryCount++; 99 | if (_instance.EntryCount > 1) 100 | { 101 | _instance.EntryCount--; 102 | return o(thisPtr, corJitInfo, methodInfo, flags, 103 | nativeEntry, nativeSizeOfCode); 104 | } 105 | 106 | 107 | var safeMethodInfo = new IntPtr((int*)methodInfo); 108 | #if NET4 109 | EHInfoHook exceptionHandlerHook = null; 110 | #endif 111 | 112 | #if NET4 113 | var functionPtr = Marshal.ReadIntPtr(Marshal.ReadIntPtr(corJitInfo), IntPtr.Size * GET_METHOD_DEF_FROM_METHOD_SLOT_INDEX); 114 | var getMethodDef = (Data.GetMethodDefFromMethodDel)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(Data.GetMethodDefFromMethodDel)); 115 | var token = getMethodDef(corJitInfo, methodInfo->ftn); 116 | #else 117 | var token = (uint)(0x06000000 | *(ushort*)methodInfo->ftn); 118 | #endif 119 | 120 | lock (_jitCompileMethodLock) 121 | { 122 | 123 | Assembly relatedAssembly = null; 124 | if (!_instance._scopeMap.ContainsKey(methodInfo->scope)) 125 | _instance._scopeMap = AppDomain.CurrentDomain.GetScopeMap(); 126 | if (_instance._scopeMap.ContainsKey(methodInfo->scope)) 127 | { 128 | relatedAssembly = _instance._scopeMap[methodInfo->scope]; 129 | } 130 | else 131 | { 132 | _instance.EntryCount--; 133 | return o(thisPtr, corJitInfo, methodInfo, flags, 134 | nativeEntry, nativeSizeOfCode); 135 | } 136 | 137 | var ra = new RawArguments 138 | { 139 | ThisPtr = thisPtr, 140 | CorJitInfo = corJitInfo, 141 | MethodInfo = safeMethodInfo, 142 | Flags = flags, 143 | NativeEntry = nativeEntry, 144 | NativeSizeOfCode = nativeSizeOfCode 145 | }; 146 | 147 | byte[] il = new byte[methodInfo->ilCodeSize]; 148 | Marshal.Copy((IntPtr)methodInfo->ilCode, il, 0, il.Length); 149 | 150 | // Extra sections contains the exception handlers. 151 | byte[] extraSections = new byte[0]; 152 | if (methodInfo->EHCount > 0) 153 | { 154 | byte* extraSectionsPtr = methodInfo->ilCode + methodInfo->ilCodeSize; 155 | extraSections = TryReadExtraSections(extraSectionsPtr); 156 | } 157 | 158 | _instance.OnCompileMethod -= OnCompileEventResetMethod; 159 | _instance.OnCompileMethod += OnCompileEventResetMethod; 160 | _instance.OnCompileMethod(ra, relatedAssembly, token, ref il, ref extraSections); 161 | _instance._compileMethodResetEvent.WaitOne(); 162 | 163 | // Assume something has changed. 164 | thisPtr = ra.ThisPtr; 165 | corJitInfo = ra.CorJitInfo; 166 | methodInfo = (Data.CorMethodInfo*)ra.MethodInfo.ToPointer(); 167 | flags = ra.Flags; 168 | nativeEntry = ra.NativeEntry; 169 | nativeSizeOfCode = ra.NativeSizeOfCode; 170 | 171 | // IL code and extra sections 172 | var ilCodeHandle = Marshal.AllocHGlobal(il.Length + (extraSections.Length * 2)); 173 | Marshal.Copy(il, 0, ilCodeHandle, il.Length); 174 | Data.VirtualProtect((IntPtr)methodInfo->ilCode, (uint)IntPtr.Size, Data.Protection.PAGE_READWRITE, 175 | out uint prevProt); 176 | methodInfo->ilCode = (byte*)ilCodeHandle.ToPointer(); 177 | methodInfo->ilCodeSize = (uint)il.Length; 178 | Marshal.Copy(extraSections, 0, (IntPtr)(methodInfo->ilCode + methodInfo->ilCodeSize), 179 | extraSections.Length); 180 | 181 | #if NET4 182 | if (methodInfo->EHCount > 0 || extraSections.Length > 2) 183 | { 184 | exceptionHandlerHook = new EHInfoHook(corJitInfo, methodInfo->ftn, il, extraSections); 185 | } 186 | #endif 187 | } 188 | 189 | var res = o(thisPtr, corJitInfo, methodInfo, flags, 190 | nativeEntry, nativeSizeOfCode); 191 | #if NET4 192 | exceptionHandlerHook?.Dispose(); 193 | #endif 194 | _instance.EntryCount--; 195 | return res; 196 | } 197 | 198 | [MethodImpl(MethodImplOptions.NoInlining)] 199 | private static int HookedCompileMethod64(IntPtr thisPtr, [In] IntPtr corJitInfo, 200 | [In] Data.CorMethodInfo64* methodInfo, Data.CorJitFlag flags, 201 | [Out] IntPtr nativeEntry, [Out] IntPtr nativeSizeOfCode) 202 | { 203 | // THIS IS THE 64 BIT COMPILE METHOD. 204 | if (thisPtr == IntPtr.Zero) return 0; 205 | 206 | var o = _instance._hookHelper.Hook.OriginalCompileMethod64; 207 | 208 | _instance.EntryCount++; 209 | if (_instance.EntryCount > 1) 210 | { 211 | _instance.EntryCount--; 212 | return o(thisPtr, corJitInfo, methodInfo, flags, 213 | nativeEntry, nativeSizeOfCode); 214 | } 215 | 216 | 217 | var safeMethodInfo = new IntPtr((int*)methodInfo); 218 | #if NET4 219 | EHInfoHook exceptionHandlerHook = null; 220 | #endif 221 | 222 | #if NET4 223 | var functionPtr = Marshal.ReadIntPtr(Marshal.ReadIntPtr(corJitInfo), IntPtr.Size * GET_METHOD_DEF_FROM_METHOD_SLOT_INDEX); 224 | var getMethodDef = (Data.GetMethodDefFromMethodDel)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(Data.GetMethodDefFromMethodDel)); 225 | var token = getMethodDef(corJitInfo, methodInfo->ftn); 226 | #else 227 | var token = (uint)(0x06000000 | *(ushort*)methodInfo->ftn); 228 | #endif 229 | 230 | lock (_jitCompileMethodLock) 231 | { 232 | 233 | Assembly relatedAssembly = null; 234 | if (!_instance._scopeMap.ContainsKey(methodInfo->scope)) 235 | _instance._scopeMap = AppDomain.CurrentDomain.GetScopeMap(); 236 | if (_instance._scopeMap.ContainsKey(methodInfo->scope)) 237 | { 238 | relatedAssembly = _instance._scopeMap[methodInfo->scope]; 239 | } 240 | else 241 | { 242 | _instance.EntryCount--; 243 | return o(thisPtr, corJitInfo, methodInfo, flags, 244 | nativeEntry, nativeSizeOfCode); 245 | } 246 | 247 | var ra = new RawArguments 248 | { 249 | ThisPtr = thisPtr, 250 | CorJitInfo = corJitInfo, 251 | MethodInfo = safeMethodInfo, 252 | Flags = flags, 253 | NativeEntry = nativeEntry, 254 | NativeSizeOfCode = nativeSizeOfCode 255 | }; 256 | 257 | byte[] il = new byte[methodInfo->ilCodeSize]; 258 | Marshal.Copy((IntPtr)methodInfo->ilCode, il, 0, il.Length); 259 | 260 | // Extra sections contains the exception handlers. 261 | byte[] extraSections = new byte[0]; 262 | if (methodInfo->EHCount > 0) 263 | { 264 | byte* extraSectionsPtr = methodInfo->ilCode + methodInfo->ilCodeSize; 265 | extraSections = TryReadExtraSections(extraSectionsPtr); 266 | } 267 | 268 | _instance.OnCompileMethod -= OnCompileEventResetMethod; 269 | _instance.OnCompileMethod += OnCompileEventResetMethod; 270 | _instance.OnCompileMethod(ra, relatedAssembly, token, ref il, ref extraSections); 271 | _instance._compileMethodResetEvent.WaitOne(); 272 | 273 | // Assume something has changed. 274 | thisPtr = ra.ThisPtr; 275 | corJitInfo = ra.CorJitInfo; 276 | methodInfo = (Data.CorMethodInfo64*)ra.MethodInfo.ToPointer(); 277 | flags = ra.Flags; 278 | nativeEntry = ra.NativeEntry; 279 | nativeSizeOfCode = ra.NativeSizeOfCode; 280 | 281 | // IL code and extra sections 282 | var ilCodeHandle = Marshal.AllocHGlobal(il.Length); 283 | Marshal.Copy(il, 0, ilCodeHandle, il.Length); 284 | Data.VirtualProtect((IntPtr)methodInfo->ilCode, (uint)IntPtr.Size, Data.Protection.PAGE_READWRITE, 285 | out uint prevProt); 286 | methodInfo->ilCode = (byte*)ilCodeHandle.ToPointer(); 287 | methodInfo->ilCodeSize = (uint)il.Length; 288 | 289 | #if NET4 290 | if (methodInfo->EHCount > 0 || extraSections.Length > 2) 291 | { 292 | exceptionHandlerHook = new EHInfoHook(corJitInfo, methodInfo->ftn, il, extraSections); 293 | } 294 | #endif 295 | } 296 | 297 | var res = o(thisPtr, corJitInfo, methodInfo, flags, 298 | nativeEntry, nativeSizeOfCode); 299 | 300 | #if NET4 301 | exceptionHandlerHook?.Dispose(); 302 | #endif 303 | _instance.EntryCount--; 304 | return res; 305 | } 306 | 307 | #region Other CorJitInfo Hooks 308 | #if NET4 309 | private class EHInfoHook 310 | { 311 | private const int SLOT_COUNT = 166; 312 | private const int SLOT_INDEX = 8; 313 | 314 | public readonly IntPtr CorJitInfoPtr; 315 | public readonly IntPtr* NewVfTable; 316 | public readonly IntPtr* OldVfTable; 317 | 318 | public readonly Data.CorInfoEhClause[] ExceptionHandlers; 319 | 320 | public readonly Data.GetEHInfoDel OriginalMethod; 321 | public readonly Data.GetEHInfoDel NewMethod; 322 | public readonly IntPtr MethodFtn; 323 | 324 | [MethodImpl(MethodImplOptions.NoInlining)] 325 | public EHInfoHook(IntPtr corJitInfo, IntPtr ftn, byte[] ilBytes, byte[] ehBytes) 326 | 327 | { 328 | MethodFtn = ftn; 329 | CorJitInfoPtr = corJitInfo; 330 | ExceptionHandlers = ehBytes.GetExceptionClauses(ilBytes.GetInstructions()).ToArray(); 331 | 332 | OldVfTable = (IntPtr*)Marshal.ReadIntPtr(corJitInfo); 333 | 334 | // Slot number, the amount of function pointers in the vftable. 335 | // Original from ConfuserEx: 158 336 | NewVfTable = (IntPtr*)Marshal.AllocHGlobal(IntPtr.Size * SLOT_COUNT); 337 | for (int i = 0; i < SLOT_COUNT; i++) 338 | { 339 | NewVfTable[i] = OldVfTable[i]; 340 | } 341 | 342 | NewMethod = Hook; 343 | OriginalMethod = 344 | (Data.GetEHInfoDel)Marshal.GetDelegateForFunctionPointer(OldVfTable[SLOT_INDEX], typeof(Data.GetEHInfoDel)); 345 | 346 | NewVfTable[SLOT_INDEX] = Marshal.GetFunctionPointerForDelegate(NewMethod); 347 | 348 | Marshal.WriteIntPtr(corJitInfo, 0, (IntPtr)NewVfTable); 349 | } 350 | 351 | [MethodImpl(MethodImplOptions.NoInlining)] 352 | public void Dispose() 353 | { 354 | Marshal.FreeHGlobal((IntPtr)NewVfTable); 355 | Marshal.WriteIntPtr(CorJitInfoPtr, 0, (IntPtr)OldVfTable); 356 | } 357 | 358 | [MethodImpl(MethodImplOptions.NoInlining)] 359 | private void Hook(IntPtr ptr, IntPtr ftn, uint ehNumber, Data.CorInfoEhClause* clause) 360 | { 361 | if (ftn == MethodFtn) 362 | { 363 | // Clause is OUT, get the exception handler bytes and get the correct clause. 364 | *clause = ExceptionHandlers[ehNumber]; 365 | } 366 | else 367 | { 368 | // The old getEHInfo method. 369 | OriginalMethod(ptr, ftn, ehNumber, clause); 370 | } 371 | } 372 | } 373 | #endif 374 | #endregion 375 | 376 | 377 | #region Extra Section Reader 378 | 379 | // Credits to 0xd4d 380 | // https://github.com/0xd4d/de4dot/blob/master/de4dot.mdecrypt/DynamicMethodsDecrypter.cs 381 | private static byte[] TryReadExtraSections(byte* p) 382 | { 383 | try 384 | { 385 | p = Align(p, 4); 386 | byte* startPos = p; 387 | p = ParseSection(p); 388 | var size = (int)(p - startPos); 389 | byte[] sections = new byte[size]; 390 | Marshal.Copy((IntPtr)startPos, sections, 0, sections.Length); 391 | return sections; 392 | } 393 | catch (Exception ex) 394 | { 395 | return new byte[0]; 396 | } 397 | } 398 | 399 | private static byte* Align(byte* p, int alignment) 400 | { 401 | return (byte*)new IntPtr((long)((ulong)(p + alignment - 1) & ~(ulong)(alignment - 1))); 402 | } 403 | 404 | private static byte* ParseSection(byte* p) 405 | { 406 | byte flags; 407 | do 408 | { 409 | p = Align(p, 4); 410 | 411 | flags = *p++; 412 | if ((flags & 1) == 0) 413 | throw new ApplicationException("Not an exception section"); 414 | if ((flags & 0x3E) != 0) 415 | throw new ApplicationException("Invalid bits set"); 416 | 417 | if ((flags & 0x40) != 0) 418 | { 419 | p--; 420 | int num = (int)(*(uint*)p >> 8) / 24; 421 | p += 4 + num * 24; 422 | } 423 | else 424 | { 425 | int num = *p++ / 12; 426 | p += 2 + num * 12; 427 | } 428 | } while ((flags & 0x80) != 0); 429 | 430 | return p; 431 | } 432 | 433 | #endregion 434 | } 435 | } -------------------------------------------------------------------------------- /TinyJitHook/Models/IHookHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using TinyJitHook.SJITHook; 4 | 5 | namespace TinyJitHook.Models 6 | { 7 | /// 8 | /// A hook helper that provides wrapping to an underlying JIT hook. 9 | /// 10 | public interface IHookHelper 11 | { 12 | /// 13 | /// The underlying JIT hook. 14 | /// 15 | IJitHook Hook { get; } 16 | 17 | /// 18 | /// The loaded assembly. 19 | /// 20 | Assembly LoadedAssembly { get; } 21 | 22 | /// 23 | /// The module scope of the manifest module. 24 | /// 25 | IntPtr ModuleScope { get; } 26 | 27 | /// 28 | /// Apply the hook. 29 | /// 30 | /// Whether the hook was applied. 31 | bool Apply(); 32 | 33 | /// 34 | /// Remove the hook. 35 | /// 36 | /// Whether the hook was removed. 37 | bool Remove(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TinyJitHook/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using TinyJitHook.Extensions; 4 | using TinyJitHook.SJITHook; 5 | 6 | namespace TinyJitHook 7 | { 8 | public class Program 9 | { 10 | public static void Main(string[] args) 11 | { 12 | Assembly asm = Assembly.LoadFrom(@"Amelia_dotnet4_x86_mod.exe"); 13 | MainJitHook hook = new MainJitHook(asm, IntPtr.Size == 8); 14 | 15 | hook.OnCompileMethod += ChangeExample; 16 | 17 | hook.Hook(); 18 | 19 | asm.EntryPoint.Invoke(null, new object[] {}); 20 | 21 | hook.Unhook(); 22 | 23 | Console.WriteLine("DONE"); 24 | Console.ReadKey(); 25 | } 26 | 27 | private static unsafe void ChangeExample(MainJitHook.RawArguments args, Assembly relatedAssembly, uint methodToken, ref byte[] ilBytes, ref byte[] ehBytes) 28 | { 29 | try 30 | { 31 | var methodBase = relatedAssembly.ManifestModule.ResolveMethod((int) methodToken); 32 | Data.CorMethodInfo* rawMethodInfo = (Data.CorMethodInfo*) args.MethodInfo.ToPointer(); 33 | bool flag = methodBase == relatedAssembly.EntryPoint; 34 | if (flag) 35 | { 36 | Console.WriteLine("REPLACING ENTRYPOINT"); 37 | //string b64 = Convert.ToBase64String(ilBytes); 38 | //string b642 = Convert.ToBase64String(ehBytes); 39 | 40 | ilBytes = Convert.FromBase64String(bytes); 41 | ehBytes = Convert.FromBase64String(bytes2); 42 | rawMethodInfo->EHCount = 1; 43 | } 44 | 45 | var insts = ilBytes.GetInstructions(); 46 | 47 | Logger.LogInfo(typeof(Program), $"---------------------------------------"); 48 | Logger.LogSuccess(typeof(Program), $"{methodBase.DeclaringType?.FullName}.{methodBase.Name}"); 49 | Logger.LogSuccess(typeof(Program), $"Inst Count: {insts.Count}"); 50 | Logger.LogSuccess(typeof(Program), $"Exception Handler Count: {rawMethodInfo->EHCount}"); 51 | 52 | if (rawMethodInfo->EHCount > 0) 53 | { 54 | var ehs = ehBytes.GetExceptionHandlers(insts); 55 | for (var i = 0; i < ehs.Count; i++) 56 | { 57 | var eh = ehs[i]; 58 | Logger.LogWarn(typeof(Program), $"Exception Handler {i + 1}:"); 59 | Logger.LogWarn(typeof(Program), $" Type: {eh.HandlerType}"); 60 | Logger.LogWarn(typeof(Program), $" TryStart: {eh.TryStart}"); 61 | Logger.LogWarn(typeof(Program), $" TryEnd: {eh.TryEnd}"); 62 | Logger.LogWarn(typeof(Program), $" CatchTypeToken: {eh.CatchTypeToken}"); 63 | } 64 | } 65 | 66 | foreach (var inst in insts) 67 | { 68 | Logger.Log(typeof(Program), $"{inst}"); 69 | } 70 | } 71 | catch (Exception ex) 72 | { 73 | // RIP 74 | } 75 | } 76 | // Token: 0x0400000B RID: 11 77 | public static string bytes = "AChNAAAKAAAWKE4AAAoAcwsAAAYoTwAACgAA3hEKAHK5AgBwBihQAAAKAADeACo="; //"AChNAAAKAAAWKE4AAAoAcwsAAAYoTwAACgAA3hEKAHK5AgBwBihQAAAKAADeACo="; 78 | 79 | // Token: 0x0400000C RID: 12 80 | public static string bytes2 = "ARAAAAAABwAWHQARHwAAAQ=="; // "ARAAAAAABwAWHQARHwAAAQ=="; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /TinyJitHook/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TinyDumper")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TinyDumper")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("30e40bd9-a0ee-451c-b24d-90b0b2485384")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TinyJitHook/SJITHook/ClrjitAddrProvider.cs: -------------------------------------------------------------------------------- 1 | /*The MIT License (MIT) 2 | 3 | Copyright (c) 2014 UbbeLoL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.*/ 22 | 23 | using System; 24 | using System.Runtime.InteropServices; 25 | 26 | namespace TinyJitHook.SJITHook 27 | { 28 | /// 29 | /// The JIT address provider for .NET 4.0 CLR. 30 | /// 31 | public sealed class ClrjitAddrProvider : VTableAddrProvider 32 | { 33 | [DllImport("Clrjit.dll", CallingConvention = CallingConvention.StdCall, PreserveSig = true)] 34 | private static extern IntPtr getJit(); 35 | 36 | /// 37 | /// Get the Virtual Table Address of the JIT. 38 | /// 39 | public override IntPtr VTableAddr 40 | { 41 | get 42 | { 43 | IntPtr pVTable = getJit(); 44 | if (pVTable == IntPtr.Zero) 45 | throw new Exception("Could not retrieve address for getJit"); 46 | 47 | return pVTable; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /TinyJitHook/SJITHook/IJitHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace TinyJitHook.SJITHook 5 | { 6 | /// 7 | /// JIT hook interface to abstract it out a bit. 8 | /// 9 | public interface IJitHook 10 | { 11 | /// 12 | /// The VTable Address that the address provider retrieved. 13 | /// 14 | IntPtr VTableAddress { get; } 15 | 16 | /// 17 | /// The original compile method (x86) if the JIT hook is for 32 bit (otherwise null). 18 | /// 19 | Data.CompileMethodDel OriginalCompileMethod32 { [MethodImpl(MethodImplOptions.NoInlining)] get; } 20 | /// 21 | /// The original compile method (x64) if the JIT hook is for 64 bit (otherwise null) 22 | /// 23 | Data.CompileMethodDel64 OriginalCompileMethod64 { [MethodImpl(MethodImplOptions.NoInlining)] get; } 24 | 25 | /// 26 | /// Hook the compileMethod function and redirect to the supplied callback. 27 | /// 28 | /// Whether it was successfully hooked. 29 | bool Hook(); 30 | /// 31 | /// Unhook the compileMethod function and stop redirection to the callback. 32 | /// 33 | /// Whether it was successfully removed. 34 | bool UnHook(); 35 | 36 | /// 37 | /// Prepare internal methods so that we do not run into loops. 38 | /// 39 | void PrepareInternalMethods(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /TinyJitHook/SJITHook/JITHook.cs: -------------------------------------------------------------------------------- 1 | /*The MIT License (MIT) 2 | 3 | Copyright (c) 2014 UbbeLoL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.*/ 22 | 23 | using System; 24 | using System.Runtime.CompilerServices; 25 | using System.Runtime.InteropServices; 26 | 27 | namespace TinyJitHook.SJITHook 28 | { 29 | /// 30 | /// A 32 bit JIT hook that works on a to redirect the pointer to compileMethod. 31 | /// 32 | /// A vtable address provider. 33 | public unsafe class JITHook : IJitHook where T : VTableAddrProvider 34 | { 35 | private readonly T _addrProvider; 36 | 37 | /// 38 | public IntPtr VTableAddress 39 | { 40 | 41 | [MethodImpl(MethodImplOptions.NoInlining)] 42 | get => pVTable; 43 | } 44 | 45 | /// 46 | /// The original compile method. 47 | /// 48 | public Data.CompileMethodDel OriginalCompileMethod32 { [MethodImpl(MethodImplOptions.NoInlining)]get; private set; } 49 | /// 50 | /// Completely unused, will return null. Do not use. 51 | /// 52 | public Data.CompileMethodDel64 OriginalCompileMethod64 { [MethodImpl(MethodImplOptions.NoInlining)]get; private set; } 53 | 54 | /// 55 | /// The callback to use instead of the original compile method. 56 | /// 57 | public Data.CompileMethodDel HookedCompileMethod { [MethodImpl(MethodImplOptions.NoInlining)]get; set; } 58 | 59 | 60 | private IntPtr pVTable; 61 | private IntPtr pCompileMethod; 62 | private uint old; 63 | 64 | /// 65 | /// Create a new 32 bit JIT hook (does not hook). Will change memory flags to PAGE_EXECUTE_READWRITE for the compileMethod region (4 or 8 bytes). 66 | /// 67 | /// The callback to replace the original. 68 | public JITHook(Data.CompileMethodDel hookedCompileMethod) 69 | { 70 | _addrProvider = Activator.CreateInstance(); 71 | HookedCompileMethod = hookedCompileMethod; 72 | 73 | pVTable = _addrProvider.VTableAddr; 74 | pCompileMethod = Marshal.ReadIntPtr(pVTable); 75 | 76 | if ( 77 | !Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 78 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 79 | throw new Exception("Cannot change memory protection flags."); 80 | 81 | OriginalCompileMethod32 = 82 | (Data.CompileMethodDel) 83 | Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(pCompileMethod), typeof(Data.CompileMethodDel)); 84 | } 85 | 86 | /// 87 | /// Hook the compileMethod function and redirect to the supplied callback. 88 | /// 89 | /// Whether it was successfully hooked. 90 | public bool Hook() 91 | { 92 | // We don't want any infinite loops :-) 93 | PrepareInternalMethods(); 94 | 95 | Marshal.WriteIntPtr(pCompileMethod, Marshal.GetFunctionPointerForDelegate(HookedCompileMethod)); 96 | 97 | return Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 98 | (Data.Protection)old, out old); 99 | } 100 | 101 | /// 102 | /// Unhook the compileMethod function and stop redirection to the callback. 103 | /// 104 | /// Whether it was successfully removed. 105 | public bool UnHook() 106 | { 107 | if ( 108 | !Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 109 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 110 | return false; 111 | 112 | Marshal.WriteIntPtr(pCompileMethod, Marshal.GetFunctionPointerForDelegate(OriginalCompileMethod32)); 113 | 114 | return Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 115 | (Data.Protection) old, out old); 116 | } 117 | 118 | /// 119 | public void PrepareInternalMethods() 120 | { 121 | RuntimeHelpers.PrepareDelegate(HookedCompileMethod); 122 | RuntimeHelpers.PrepareDelegate(OriginalCompileMethod32); 123 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_OriginalCompileMethod64").MethodHandle, new[] { typeof(T).TypeHandle }); 124 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_OriginalCompileMethod32").MethodHandle, new[] { typeof(T).TypeHandle }); 125 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_HookedCompileMethod").MethodHandle, new[] { typeof(T).TypeHandle }); 126 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_VTableAddress").MethodHandle, new[] { typeof(T).TypeHandle }); 127 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("UnHook").MethodHandle, new[] { typeof(T).TypeHandle }); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /TinyJitHook/SJITHook/JITHook64.cs: -------------------------------------------------------------------------------- 1 | /*The MIT License (MIT) 2 | 3 | Copyright (c) 2014 UbbeLoL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.*/ 22 | 23 | using System; 24 | using System.Runtime.CompilerServices; 25 | using System.Runtime.InteropServices; 26 | 27 | namespace TinyJitHook.SJITHook 28 | { 29 | /// 30 | /// A 64 bit JIT hook that works on a to redirect the pointer to compileMethod. 31 | /// 32 | /// A vtable address provider. 33 | public unsafe class JITHook64 : IJitHook where T : VTableAddrProvider 34 | { 35 | /// 36 | public IntPtr VTableAddress 37 | { 38 | 39 | [MethodImpl(MethodImplOptions.NoInlining)] 40 | get => pVTable; 41 | } 42 | 43 | private readonly T _addrProvider; 44 | /// 45 | /// The original compile method. 46 | /// 47 | 48 | public Data.CompileMethodDel64 OriginalCompileMethod64 { [MethodImpl(MethodImplOptions.NoInlining)] get; private set; } 49 | 50 | /// 51 | /// Completely unused, will return null. Do not use. 52 | /// 53 | public Data.CompileMethodDel OriginalCompileMethod32 { [MethodImpl(MethodImplOptions.NoInlining)] get; private set; } 54 | 55 | /// 56 | /// The callback to use instead of the original compile method. 57 | /// 58 | public Data.CompileMethodDel64 HookedCompileMethod { [MethodImpl(MethodImplOptions.NoInlining)] get; set; } 59 | 60 | private IntPtr pVTable; 61 | private IntPtr pCompileMethod; 62 | private uint old; 63 | 64 | /// 65 | /// Create a new 64 bit JIT hook (does not hook). Will change memory flags to PAGE_EXECUTE_READWRITE for the compileMethod region (4 or 8 bytes). 66 | /// 67 | /// The callback to replace the original. 68 | public JITHook64(Data.CompileMethodDel64 hookedCompileMethod) 69 | { 70 | _addrProvider = Activator.CreateInstance(); 71 | HookedCompileMethod = hookedCompileMethod; 72 | 73 | pVTable = _addrProvider.VTableAddr; 74 | pCompileMethod = Marshal.ReadIntPtr(pVTable); 75 | 76 | if ( 77 | !Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 78 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 79 | throw new Exception("Cannot change memory protection flags."); 80 | 81 | OriginalCompileMethod64 = 82 | (Data.CompileMethodDel64) 83 | Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(pCompileMethod), typeof(Data.CompileMethodDel64)); 84 | Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 85 | (Data.Protection)old, out old); 86 | } 87 | 88 | /// 89 | /// Hook the compileMethod function and redirect to the supplied callback. 90 | /// 91 | /// Whether it was successfully hooked. 92 | public bool Hook() 93 | { 94 | 95 | // We don't want any infinite loops :-) 96 | PrepareInternalMethods(); 97 | 98 | if ( 99 | !Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 100 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 101 | throw new Exception("Cannot change memory protection flags."); 102 | 103 | Marshal.WriteIntPtr(pCompileMethod, Marshal.GetFunctionPointerForDelegate(HookedCompileMethod)); 104 | 105 | return Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 106 | (Data.Protection)old, out old); 107 | } 108 | 109 | /// 110 | /// Unhook the compileMethod function and stop redirection to the callback. 111 | /// 112 | /// Whether it was successfully removed. 113 | public bool UnHook() 114 | { 115 | IntPtr pVTable = _addrProvider.VTableAddr; 116 | IntPtr pCompileMethod = Marshal.ReadIntPtr(pVTable); 117 | uint old; 118 | 119 | if ( 120 | !Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 121 | Data.Protection.PAGE_EXECUTE_READWRITE, out old)) 122 | return false; 123 | 124 | Marshal.WriteIntPtr(pCompileMethod, Marshal.GetFunctionPointerForDelegate(OriginalCompileMethod64)); 125 | 126 | return Data.VirtualProtect(pCompileMethod, (uint)IntPtr.Size, 127 | (Data.Protection) old, out old); 128 | } 129 | 130 | /// 131 | public void PrepareInternalMethods() 132 | { 133 | RuntimeHelpers.PrepareDelegate(HookedCompileMethod); 134 | RuntimeHelpers.PrepareDelegate(OriginalCompileMethod64); 135 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_OriginalCompileMethod64").MethodHandle, new[] { typeof(T).TypeHandle }); 136 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_OriginalCompileMethod32").MethodHandle, new[] { typeof(T).TypeHandle }); 137 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_HookedCompileMethod").MethodHandle, new[] { typeof(T).TypeHandle }); 138 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("get_VTableAddress").MethodHandle, new[] { typeof(T).TypeHandle }); 139 | RuntimeHelpers.PrepareMethod(GetType().GetMethod("UnHook").MethodHandle, new[] { typeof(T).TypeHandle }); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /TinyJitHook/SJITHook/MscorjitAddrProvider.cs: -------------------------------------------------------------------------------- 1 | /*The MIT License (MIT) 2 | 3 | Copyright (c) 2014 UbbeLoL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.*/ 22 | 23 | using System; 24 | using System.Runtime.InteropServices; 25 | 26 | namespace TinyJitHook.SJITHook 27 | { 28 | /// 29 | /// The JIT address provider for .NET 2.0 CLR. 30 | /// 31 | public sealed class MscorjitAddrProvider : VTableAddrProvider 32 | { 33 | [DllImport("mscorjit.dll", CallingConvention = CallingConvention.StdCall, PreserveSig = true)] 34 | private static extern IntPtr getJit(); 35 | 36 | /// 37 | /// Get the Virtual Table Address of the JIT. 38 | /// 39 | public override IntPtr VTableAddr 40 | { 41 | get 42 | { 43 | IntPtr pVTable = getJit(); 44 | if (pVTable == IntPtr.Zero) 45 | throw new Exception("Could not retrieve address for getJit"); 46 | 47 | return pVTable; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /TinyJitHook/SJITHook/VTableAddrProvider.cs: -------------------------------------------------------------------------------- 1 | /*The MIT License (MIT) 2 | 3 | Copyright (c) 2014 UbbeLoL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.*/ 22 | 23 | using System; 24 | 25 | namespace TinyJitHook.SJITHook 26 | { 27 | /// 28 | /// The VTableAddressProvider class will provide an address for the vtable for a particular CLR version. 29 | /// 30 | public abstract class VTableAddrProvider 31 | { 32 | internal delegate IntPtr GetJit(); 33 | /// 34 | /// Get the Virtual Table Address of the JIT. 35 | /// 36 | public abstract IntPtr VTableAddr { get; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /TinyJitHook/TinyJitHook.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {30E40BD9-A0EE-451C-B24D-90B0B2485384} 8 | Exe 9 | Properties 10 | TinyJitHook 11 | TinyJitHook 12 | v4.0 13 | 512 14 | true 15 | 16 | 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | ..\Debug\ 24 | TRACE;DEBUG 25 | prompt 26 | 4 27 | true 28 | 29 | 30 | false 31 | 32 | 33 | AnyCPU 34 | pdbonly 35 | true 36 | bin\Release\ 37 | TRACE 38 | prompt 39 | 4 40 | false 41 | 42 | 43 | true 44 | ..\Debug\ 45 | TRACE;DEBUG;NET4 46 | true 47 | full 48 | x86 49 | prompt 50 | MinimumRecommendedRules.ruleset 51 | true 52 | false 53 | 54 | 55 | bin\x86\Release\ 56 | TRACE 57 | true 58 | pdbonly 59 | x86 60 | prompt 61 | MinimumRecommendedRules.ruleset 62 | true 63 | 64 | 65 | true 66 | ..\Debug\ 67 | TRACE;DEBUG;NET4 68 | true 69 | full 70 | x64 71 | prompt 72 | MinimumRecommendedRules.ruleset 73 | false 74 | 75 | 76 | bin\x64\Release\ 77 | TRACE 78 | true 79 | pdbonly 80 | x64 81 | prompt 82 | MinimumRecommendedRules.ruleset 83 | false 84 | true 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 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 129 | --------------------------------------------------------------------------------