├── .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