├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
└── src
├── ExampleAssembly
├── Cheat.cs
├── ExampleAssembly.csproj
├── Loader.cs
└── Properties
│ └── AssemblyInfo.cs
├── SharpMonoInjector.Console
├── App.config
├── CommandLineArguments.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── SharpMonoInjector.Console.csproj
├── SharpMonoInjector.Gui
├── App.config
├── App.xaml
├── App.xaml.cs
├── Converters
│ ├── InjectedAssemblyToStringConverter.cs
│ └── MonoProcessToStringConverter.cs
├── Models
│ ├── InjectedAssembly.cs
│ └── MonoProcess.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── SharpMonoInjector.Gui.csproj
├── ViewModels
│ ├── MainWindowViewModel.cs
│ ├── RelayCommand.cs
│ └── ViewModel.cs
└── Views
│ ├── MainWindow.xaml
│ └── MainWindow.xaml.cs
├── SharpMonoInjector.sln
└── SharpMonoInjector
├── Assembler.cs
├── ExportedFunction.cs
├── Injector.cs
├── InjectorException.cs
├── Memory.cs
├── MonoImageOpenStatus.cs
├── Native.cs
├── ProcessUtils.cs
└── SharpMonoInjector.csproj
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
--------------------------------------------------------------------------------
/.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 | build/
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # MSTest test Results
34 | [Tt]est[Rr]esult*/
35 | [Bb]uild[Ll]og.*
36 |
37 | # NUNIT
38 | *.VisualState.xml
39 | TestResult.xml
40 |
41 | # Build Results of an ATL Project
42 | [Dd]ebugPS/
43 | [Rr]eleasePS/
44 | dlldata.c
45 |
46 | # Benchmark Results
47 | BenchmarkDotNet.Artifacts/
48 |
49 | # .NET Core
50 | project.lock.json
51 | project.fragment.lock.json
52 | artifacts/
53 | **/Properties/launchSettings.json
54 |
55 | *_i.c
56 | *_p.c
57 | *_i.h
58 | *.ilk
59 | *.meta
60 | *.obj
61 | *.pch
62 | *.pdb
63 | *.pgc
64 | *.pgd
65 | *.rsp
66 | *.sbr
67 | *.tlb
68 | *.tli
69 | *.tlh
70 | *.tmp
71 | *.tmp_proj
72 | *.log
73 | *.vspscc
74 | *.vssscc
75 | .builds
76 | *.pidb
77 | *.svclog
78 | *.scc
79 |
80 | # Chutzpah Test files
81 | _Chutzpah*
82 |
83 | # Visual C++ cache files
84 | ipch/
85 | *.aps
86 | *.ncb
87 | *.opendb
88 | *.opensdf
89 | *.sdf
90 | *.cachefile
91 | *.VC.db
92 | *.VC.VC.opendb
93 |
94 | # Visual Studio profiler
95 | *.psess
96 | *.vsp
97 | *.vspx
98 | *.sap
99 |
100 | # TFS 2012 Local Workspace
101 | $tf/
102 |
103 | # Guidance Automation Toolkit
104 | *.gpState
105 |
106 | # ReSharper is a .NET coding add-in
107 | _ReSharper*/
108 | *.[Rr]e[Ss]harper
109 | *.DotSettings.user
110 |
111 | # JustCode is a .NET coding add-in
112 | .JustCode
113 |
114 | # TeamCity is a build add-in
115 | _TeamCity*
116 |
117 | # DotCover is a Code Coverage Tool
118 | *.dotCover
119 |
120 | # AxoCover is a Code Coverage Tool
121 | .axoCover/*
122 | !.axoCover/settings.json
123 |
124 | # Visual Studio code coverage results
125 | *.coverage
126 | *.coveragexml
127 |
128 | # NCrunch
129 | _NCrunch_*
130 | .*crunch*.local.xml
131 | nCrunchTemp_*
132 |
133 | # MightyMoose
134 | *.mm.*
135 | AutoTest.Net/
136 |
137 | # Web workbench (sass)
138 | .sass-cache/
139 |
140 | # Installshield output folder
141 | [Ee]xpress/
142 |
143 | # DocProject is a documentation generator add-in
144 | DocProject/buildhelp/
145 | DocProject/Help/*.HxT
146 | DocProject/Help/*.HxC
147 | DocProject/Help/*.hhc
148 | DocProject/Help/*.hhk
149 | DocProject/Help/*.hhp
150 | DocProject/Help/Html2
151 | DocProject/Help/html
152 |
153 | # Click-Once directory
154 | publish/
155 |
156 | # Publish Web Output
157 | *.[Pp]ublish.xml
158 | *.azurePubxml
159 | # Note: Comment the next line if you want to checkin your web deploy settings,
160 | # but database connection strings (with potential passwords) will be unencrypted
161 | *.pubxml
162 | *.publishproj
163 |
164 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
165 | # checkin your Azure Web App publish settings, but sensitive information contained
166 | # in these scripts will be unencrypted
167 | PublishScripts/
168 |
169 | # NuGet Packages
170 | *.nupkg
171 | # The packages folder can be ignored because of Package Restore
172 | **/packages/*
173 | # except build/, which is used as an MSBuild target.
174 | !**/packages/build/
175 | # Uncomment if necessary however generally it will be regenerated when needed
176 | #!**/packages/repositories.config
177 | # NuGet v3's project.json files produces more ignorable files
178 | *.nuget.props
179 | *.nuget.targets
180 |
181 | # Microsoft Azure Build Output
182 | csx/
183 | *.build.csdef
184 |
185 | # Microsoft Azure Emulator
186 | ecf/
187 | rcf/
188 |
189 | # Windows Store app package directories and files
190 | AppPackages/
191 | BundleArtifacts/
192 | Package.StoreAssociation.xml
193 | _pkginfo.txt
194 | *.appx
195 |
196 | # Visual Studio cache files
197 | # files ending in .cache can be ignored
198 | *.[Cc]ache
199 | # but keep track of directories ending in .cache
200 | !*.[Cc]ache/
201 |
202 | # Others
203 | ClientBin/
204 | ~$*
205 | *~
206 | *.dbmdl
207 | *.dbproj.schemaview
208 | *.jfm
209 | *.pfx
210 | *.publishsettings
211 | orleans.codegen.cs
212 |
213 | # Since there are multiple workflows, uncomment next line to ignore bower_components
214 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
215 | #bower_components/
216 |
217 | # RIA/Silverlight projects
218 | Generated_Code/
219 |
220 | # Backup & report files from converting an old project file
221 | # to a newer Visual Studio version. Backup files are not needed,
222 | # because we have git ;-)
223 | _UpgradeReport_Files/
224 | Backup*/
225 | UpgradeLog*.XML
226 | UpgradeLog*.htm
227 |
228 | # SQL Server files
229 | *.mdf
230 | *.ldf
231 | *.ndf
232 |
233 | # Business Intelligence projects
234 | *.rdl.data
235 | *.bim.layout
236 | *.bim_*.settings
237 |
238 | # Microsoft Fakes
239 | FakesAssemblies/
240 |
241 | # GhostDoc plugin setting file
242 | *.GhostDoc.xml
243 |
244 | # Node.js Tools for Visual Studio
245 | .ntvs_analysis.dat
246 | node_modules/
247 |
248 | # Typescript v1 declaration files
249 | typings/
250 |
251 | # Visual Studio 6 build log
252 | *.plg
253 |
254 | # Visual Studio 6 workspace options file
255 | *.opt
256 |
257 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
258 | *.vbw
259 |
260 | # Visual Studio LightSwitch build output
261 | **/*.HTMLClient/GeneratedArtifacts
262 | **/*.DesktopClient/GeneratedArtifacts
263 | **/*.DesktopClient/ModelManifest.xml
264 | **/*.Server/GeneratedArtifacts
265 | **/*.Server/ModelManifest.xml
266 | _Pvt_Extensions
267 |
268 | # Paket dependency manager
269 | .paket/paket.exe
270 | paket-files/
271 |
272 | # FAKE - F# Make
273 | .fake/
274 |
275 | # JetBrains Rider
276 | .idea/
277 | *.sln.iml
278 |
279 | # CodeRush
280 | .cr/
281 |
282 | # Python Tools for Visual Studio (PTVS)
283 | __pycache__/
284 | *.pyc
285 |
286 | # Cake - Uncomment if you are using it
287 | # tools/**
288 | # !tools/packages.config
289 |
290 | # Tabs Studio
291 | *.tss
292 |
293 | # Telerik's JustMock configuration file
294 | *.jmconfig
295 |
296 | # BizTalk build output
297 | *.btp.cs
298 | *.btm.cs
299 | *.odx.cs
300 | *.xsd.cs
301 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Biney
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SharpMonoInjector
2 | SharpMonoInjector is a tool for injecting assemblies into Mono embedded applications, commonly Unity Engine based games. The target process *usually* does not have to be restarted in order to inject an updated version of the assembly. Your unload method must to destroy all of its resources (such as game objects).
3 |
4 | SharpMonoInjector works by dynamically generating machine code, writing it to the target process and executing it using CreateRemoteThread. The code calls functions in the mono embedded API. The return value is obtained with ReadProcessMemory.
5 |
6 | Both x86 and x64 processes are supported.
7 |
8 | In order for the injector to work, the load/unload methods need to match the following method signature:
9 |
10 | static void Method()
11 |
12 | In [releases](https://github.com/warbler/SharpMonoInjector/releases), there is a console application and a GUI application available.
13 |
14 | 
15 | 
16 |
--------------------------------------------------------------------------------
/src/ExampleAssembly/Cheat.cs:
--------------------------------------------------------------------------------
1 | namespace ExampleAssembly
2 | {
3 | public class Cheat : UnityEngine.MonoBehaviour
4 | {
5 | private void OnGUI()
6 | {
7 | UnityEngine.GUI.Label(new UnityEngine.Rect(10, 10, 200, 40), "This is a very useful cheat");
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/ExampleAssembly/ExampleAssembly.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {251A7FF8-4D39-4F8B-9838-61124BF62F99}
8 | Library
9 | Properties
10 | ExampleAssembly
11 | ExampleAssembly
12 | v4.6.1
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | ..\..\build\debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 | AnyCPU
24 |
25 |
26 | pdbonly
27 | true
28 | ..\..\build\release\
29 | TRACE
30 | prompt
31 | 4
32 | AnyCPU
33 |
34 |
35 | true
36 | bin\x64\Debug\
37 | DEBUG;TRACE
38 | full
39 | x64
40 | prompt
41 | MinimumRecommendedRules.ruleset
42 |
43 |
44 | bin\x64\Release\
45 | TRACE
46 | true
47 | pdbonly
48 | x64
49 | prompt
50 | MinimumRecommendedRules.ruleset
51 |
52 |
53 |
54 |
55 |
56 | ..\..\lib\UnityEngine.dll
57 | False
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/src/ExampleAssembly/Loader.cs:
--------------------------------------------------------------------------------
1 | namespace ExampleAssembly
2 | {
3 | public class Loader
4 | {
5 | static UnityEngine.GameObject gameObject;
6 |
7 | public static void Load()
8 | {
9 | gameObject = new UnityEngine.GameObject();
10 | gameObject.AddComponent();
11 | UnityEngine.Object.DontDestroyOnLoad(gameObject);
12 | }
13 |
14 | public static void Unload()
15 | {
16 | UnityEngine.Object.Destroy(gameObject);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/ExampleAssembly/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("ExampleAssembly")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Biney")]
12 | [assembly: AssemblyProduct("ExampleAssembly")]
13 | [assembly: AssemblyCopyright("Copyright © Biney")]
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("251a7ff8-4d39-4f8b-9838-61124bf62f99")]
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 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Console/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Console/CommandLineArguments.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Linq;
3 |
4 | namespace SharpMonoInjector.Console
5 | {
6 | public class CommandLineArguments
7 | {
8 | private readonly string[] _args;
9 |
10 | public CommandLineArguments(string[] args)
11 | {
12 | _args = args;
13 | }
14 |
15 | public bool IsSwitchPresent(string name) => _args.Any(arg => arg == name);
16 |
17 | public bool GetLongArg(string name, out long value)
18 | {
19 | if (GetStringArg(name, out string str))
20 | return long.TryParse(str.StartsWith("0x") ? str.Substring(2) : str, NumberStyles.AllowHexSpecifier, null, out value);
21 |
22 | value = default(long);
23 | return false;
24 | }
25 |
26 | public bool GetIntArg(string name, out int value)
27 | {
28 | if (GetStringArg(name, out string str))
29 | return int.TryParse(str.StartsWith("0x") ? str.Substring(2) : str, NumberStyles.AllowHexSpecifier, null, out value);
30 |
31 | value = default(int);
32 | return false;
33 | }
34 |
35 | public bool GetStringArg(string name, out string value)
36 | {
37 | for (int i = 0; i < _args.Length; i++) {
38 | string arg = _args[i];
39 |
40 | if (arg == name) {
41 | if (i == _args.Length - 1)
42 | break;
43 |
44 | value = _args[i + 1];
45 | return true;
46 | }
47 | }
48 |
49 | value = null;
50 | return false;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Console/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace SharpMonoInjector.Console
5 | {
6 | internal static class Program
7 | {
8 | private static void Main(string[] args)
9 | {
10 | if (args.Length == 0) {
11 | PrintHelp();
12 | return;
13 | }
14 |
15 | CommandLineArguments cla = new CommandLineArguments(args);
16 |
17 | bool inject = cla.IsSwitchPresent("inject");
18 | bool eject = cla.IsSwitchPresent("eject");
19 |
20 | if (!inject && !eject) {
21 | System.Console.WriteLine("No operation (inject/eject) specified");
22 | return;
23 | }
24 |
25 | Injector injector;
26 |
27 | if (cla.GetIntArg("-p", out int pid)) {
28 | injector = new Injector(pid);
29 | } else if (cla.GetStringArg("-p", out string pname)) {
30 | injector = new Injector(pname);
31 | } else {
32 | System.Console.WriteLine("No process id/name specified");
33 | return;
34 | }
35 |
36 | if (inject)
37 | Inject(injector, cla);
38 | else
39 | Eject(injector, cla);
40 | }
41 |
42 | private static void PrintHelp()
43 | {
44 | const string help =
45 | "SharpMonoInjector 2.2\r\n\r\n" +
46 | "Usage:\r\n" +
47 | "smi.exe \r\n\r\n" +
48 | "Options:\r\n" +
49 | "-p - The id or name of the target process\r\n" +
50 | "-a - When injecting, the path of the assembly to inject. When ejecting, the address of the assembly to eject\r\n" +
51 | "-n - The namespace in which the loader class resides\r\n" +
52 | "-c - The name of the loader class\r\n" +
53 | "-m - The name of the method to invoke in the loader class\r\n\r\n" +
54 | "Examples:\r\n" +
55 | "smi.exe inject -p testgame -a ExampleAssembly.dll -n ExampleAssembly -c Loader -m Load\r\n" +
56 | "smi.exe eject -p testgame -a 0x13D23A98 -n ExampleAssembly -c Loader -m Unload\r\n";
57 | System.Console.WriteLine(help);
58 | }
59 |
60 | private static void Inject(Injector injector, CommandLineArguments args)
61 | {
62 | string assemblyPath, @namespace, className, methodName;
63 | byte[] assembly;
64 |
65 | if (args.GetStringArg("-a", out assemblyPath)) {
66 | try {
67 | assembly = File.ReadAllBytes(assemblyPath);
68 | } catch {
69 | System.Console.WriteLine("Could not read the file " + assemblyPath);
70 | return;
71 | }
72 | } else {
73 | System.Console.WriteLine("No assembly specified");
74 | return;
75 | }
76 |
77 | args.GetStringArg("-n", out @namespace);
78 |
79 | if (!args.GetStringArg("-c", out className)) {
80 | System.Console.WriteLine("No class name specified");
81 | return;
82 | }
83 |
84 | if (!args.GetStringArg("-m", out methodName)) {
85 | System.Console.WriteLine("No method name specified");
86 | return;
87 | }
88 |
89 | using (injector) {
90 | IntPtr remoteAssembly = IntPtr.Zero;
91 |
92 | try {
93 | remoteAssembly = injector.Inject(assembly, @namespace, className, methodName);
94 | } catch (InjectorException ie) {
95 | System.Console.WriteLine("Failed to inject assembly: " + ie);
96 | } catch (Exception exc) {
97 | System.Console.WriteLine("Failed to inject assembly (unknown error): " + exc);
98 | }
99 |
100 | if (remoteAssembly == IntPtr.Zero)
101 | return;
102 |
103 | System.Console.WriteLine($"{Path.GetFileName(assemblyPath)}: " +
104 | (injector.Is64Bit
105 | ? $"0x{remoteAssembly.ToInt64():X16}"
106 | : $"0x{remoteAssembly.ToInt32():X8}"));
107 | }
108 | }
109 |
110 | private static void Eject(Injector injector, CommandLineArguments args)
111 | {
112 | IntPtr assembly;
113 | string @namespace, className, methodName;
114 |
115 | if (args.GetIntArg("-a", out int intPtr)) {
116 | assembly = (IntPtr)intPtr;
117 | } else if (args.GetLongArg("-a", out long longPtr)) {
118 | assembly = (IntPtr)longPtr;
119 | } else {
120 | System.Console.WriteLine("No assembly pointer specified");
121 | return;
122 | }
123 |
124 | args.GetStringArg("-n", out @namespace);
125 |
126 | if (!args.GetStringArg("-c", out className)) {
127 | System.Console.WriteLine("No class name specified");
128 | return;
129 | }
130 |
131 | if (!args.GetStringArg("-m", out methodName)) {
132 | System.Console.WriteLine("No method name specified");
133 | return;
134 | }
135 |
136 | using (injector) {
137 | try {
138 | injector.Eject(assembly, @namespace, className, methodName);
139 | System.Console.WriteLine("Ejection successful");
140 | } catch (InjectorException ie) {
141 | System.Console.WriteLine("Ejection failed: " + ie);
142 | } catch (Exception exc) {
143 | System.Console.WriteLine("Ejection failed (unknown error): " + exc);
144 | }
145 | }
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Console/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("SharpMonoInjector.Console")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("SharpMonoInjector.Console")]
13 | [assembly: AssemblyCopyright("Copyright © 2019")]
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("e677de19-10c7-4981-bc7a-f49ae07e3391")]
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 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Console/SharpMonoInjector.Console.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {E677DE19-10C7-4981-BC7A-F49AE07E3391}
8 | Exe
9 | SharpMonoInjector.Console
10 | smi
11 | v4.7.1
12 | 512
13 | true
14 | true
15 |
16 |
17 |
18 | AnyCPU
19 | true
20 | full
21 | false
22 | ..\..\build\debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 | false
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | ..\..\build\release\
33 | TRACE
34 | prompt
35 | 4
36 | false
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | {9a2f2e9f-d314-4b7a-ba8d-8522bb91a114}
53 | SharpMonoInjector
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace SharpMonoInjector.Gui
10 | {
11 | ///
12 | /// Interaction logic for App.xaml
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/Converters/InjectedAssemblyToStringConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 | using SharpMonoInjector.Gui.Models;
5 |
6 | namespace SharpMonoInjector.Gui.Converters
7 | {
8 | public class InjectedAssemblyToStringConverter : IValueConverter
9 | {
10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
11 | {
12 | if (value == null)
13 | return null;
14 |
15 | InjectedAssembly ia = (InjectedAssembly)value;
16 | return $"[{(ia.Is64Bit ? $"0x{ia.Address.ToInt64():X16}" : $"0x{ia.Address.ToInt32():X8}")}] {ia.Name}";
17 | }
18 |
19 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
20 | {
21 | throw new NotImplementedException();
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/Converters/MonoProcessToStringConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 | using SharpMonoInjector.Gui.Models;
5 |
6 | namespace SharpMonoInjector.Gui.Converters
7 | {
8 | public class MonoProcessToStringConverter : IValueConverter
9 | {
10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
11 | {
12 | if (value == null)
13 | return null;
14 |
15 | MonoProcess mp = (MonoProcess)value;
16 | return $"[{mp.Id}] {mp.Name}";
17 | }
18 |
19 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
20 | {
21 | throw new NotImplementedException();
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/Models/InjectedAssembly.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SharpMonoInjector.Gui.Models
4 | {
5 | public class InjectedAssembly
6 | {
7 | public int ProcessId { get; set; }
8 |
9 | public IntPtr Address { get; set; }
10 |
11 | public bool Is64Bit { get; set; }
12 |
13 | public string Name { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/Models/MonoProcess.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SharpMonoInjector.Gui.Models
4 | {
5 | public class MonoProcess
6 | {
7 | public IntPtr MonoModule { get; set; }
8 |
9 | public string Name { get; set; }
10 |
11 | public int Id { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("SharpMonoInjector.Gui")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("SharpMonoInjector.Gui")]
15 | [assembly: AssemblyCopyright("Copyright © 2019")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SharpMonoInjector.Gui.Properties
12 | {
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// Returns the cached ResourceManager instance used by this class.
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get {
44 | if ((resourceMan == null)) {
45 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SharpMonoInjector.Gui.Properties.Resources", typeof(Resources).Assembly);
46 | resourceMan = temp;
47 | }
48 | return resourceMan;
49 | }
50 | }
51 |
52 | ///
53 | /// Overrides the current thread's CurrentUICulture property for all
54 | /// resource lookups using this strongly typed resource class.
55 | ///
56 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
57 | internal static global::System.Globalization.CultureInfo Culture
58 | {
59 | get {
60 | return resourceCulture;
61 | }
62 | set {
63 | resourceCulture = value;
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SharpMonoInjector.Gui.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get {
25 | return defaultInstance;
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/SharpMonoInjector.Gui.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {AA64466B-D036-47B1-AA77-DB1DAA026313}
8 | WinExe
9 | SharpMonoInjector.Gui
10 | smi_gui
11 | v4.7.1
12 | 512
13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 4
15 | true
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | ..\..\build\debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 | false
28 |
29 |
30 | AnyCPU
31 | pdbonly
32 | true
33 | ..\..\build\release\
34 | TRACE
35 | prompt
36 | 4
37 | false
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | 4.0
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | MSBuild:Compile
58 | Designer
59 |
60 |
61 | MSBuild:Compile
62 | Designer
63 |
64 |
65 | App.xaml
66 | Code
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | MainWindow.xaml
77 | Code
78 |
79 |
80 |
81 |
82 | Code
83 |
84 |
85 | True
86 | True
87 | Resources.resx
88 |
89 |
90 | True
91 | Settings.settings
92 | True
93 |
94 |
95 | ResXFileCodeGenerator
96 | Resources.Designer.cs
97 | Designer
98 |
99 |
100 | SettingsSingleFileGenerator
101 | Settings.Designer.cs
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | {9a2f2e9f-d314-4b7a-ba8d-8522bb91a114}
110 | SharpMonoInjector
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/ViewModels/MainWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.ObjectModel;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 | using Microsoft.Win32;
8 | using SharpMonoInjector.Gui.Models;
9 |
10 | namespace SharpMonoInjector.Gui.ViewModels
11 | {
12 | public class MainWindowViewModel : ViewModel
13 | {
14 | public RelayCommand RefreshCommand { get; }
15 |
16 | public RelayCommand BrowseCommand { get; }
17 |
18 | public RelayCommand InjectCommand { get; }
19 |
20 | public RelayCommand EjectCommand { get; }
21 |
22 | public RelayCommand CopyStatusCommand { get; }
23 |
24 | public MainWindowViewModel()
25 | {
26 | RefreshCommand = new RelayCommand(ExecuteRefreshCommand, CanExecuteRefreshCommand);
27 | BrowseCommand = new RelayCommand(ExecuteBrowseCommand);
28 | InjectCommand = new RelayCommand(ExecuteInjectCommand, CanExecuteInjectCommand);
29 | EjectCommand = new RelayCommand(ExecuteEjectCommand, CanExecuteEjectCommand);
30 | CopyStatusCommand = new RelayCommand(ExecuteCopyStatusCommand);
31 | }
32 |
33 | private void ExecuteCopyStatusCommand(object parameter)
34 | {
35 | Clipboard.SetText(Status);
36 | }
37 |
38 | private bool CanExecuteRefreshCommand(object parameter)
39 | {
40 | return !IsRefreshing;
41 | }
42 |
43 | private async void ExecuteRefreshCommand(object parameter)
44 | {
45 | IsRefreshing = true;
46 | Status = "Refreshing processes";
47 | ObservableCollection processes = new ObservableCollection();
48 |
49 | await Task.Run(() =>
50 | {
51 | int cp = Process.GetCurrentProcess().Id;
52 |
53 | foreach (Process p in Process.GetProcesses()) {
54 | if (p.Id == cp)
55 | continue;
56 |
57 | const ProcessAccessRights flags = ProcessAccessRights.PROCESS_QUERY_INFORMATION | ProcessAccessRights.PROCESS_VM_READ;
58 | IntPtr handle;
59 |
60 | if ((handle = Native.OpenProcess(flags, false, p.Id)) != IntPtr.Zero) {
61 | if (ProcessUtils.GetMonoModule(handle, out IntPtr mono)) {
62 | processes.Add(new MonoProcess
63 | {
64 | MonoModule = mono,
65 | Id = p.Id,
66 | Name = p.ProcessName
67 | });
68 | }
69 |
70 | Native.CloseHandle(handle);
71 | }
72 | }
73 | });
74 |
75 | Processes = processes;
76 |
77 | if (Processes.Count > 0)
78 | SelectedProcess = Processes[0];
79 |
80 | IsRefreshing = false;
81 | Status = "Processes refreshed";
82 | }
83 |
84 | private void ExecuteBrowseCommand(object parameter)
85 | {
86 | OpenFileDialog ofd = new OpenFileDialog();
87 | ofd.Filter = "Dynamic Link Library|*.dll";
88 | ofd.Title = "Select assembly to inject";
89 |
90 | if (ofd.ShowDialog() == true)
91 | AssemblyPath = ofd.FileName;
92 | }
93 |
94 | private bool CanExecuteInjectCommand(object parameter)
95 | {
96 | return SelectedProcess != null &&
97 | File.Exists(AssemblyPath) &&
98 | !string.IsNullOrEmpty(InjectClassName) &&
99 | !string.IsNullOrEmpty(InjectMethodName) &&
100 | !IsExecuting;
101 | }
102 |
103 | private void ExecuteInjectCommand(object parameter)
104 | {
105 | IntPtr handle = Native.OpenProcess(ProcessAccessRights.PROCESS_ALL_ACCESS, false, SelectedProcess.Id);
106 |
107 | if (handle == IntPtr.Zero) {
108 | Status = "Failed to open process";
109 | return;
110 | }
111 |
112 | byte[] file;
113 |
114 | try {
115 | file = File.ReadAllBytes(AssemblyPath);
116 | } catch (IOException) {
117 | Status = "Failed to read the file " + AssemblyPath;
118 | return;
119 | }
120 |
121 | IsExecuting = true;
122 | Status = "Injecting " + Path.GetFileName(AssemblyPath);
123 |
124 | using (Injector injector = new Injector(handle, SelectedProcess.MonoModule)) {
125 | try {
126 | IntPtr asm = injector.Inject(file, InjectNamespace, InjectClassName, InjectMethodName);
127 | InjectedAssemblies.Add(new InjectedAssembly
128 | {
129 | ProcessId = SelectedProcess.Id,
130 | Address = asm,
131 | Name = Path.GetFileName(AssemblyPath),
132 | Is64Bit = injector.Is64Bit
133 | });
134 | Status = "Injection successful";
135 | } catch (InjectorException ie) {
136 | Status = "Injection failed: " + ie.Message;
137 | } catch (Exception e) {
138 | Status = "Injection failed (unknown error): " + e.Message;
139 | }
140 | }
141 |
142 | IsExecuting = false;
143 | }
144 |
145 | private bool CanExecuteEjectCommand(object parameter)
146 | {
147 | return SelectedAssembly != null &&
148 | !string.IsNullOrEmpty(EjectClassName) &&
149 | !string.IsNullOrEmpty(EjectMethodName) &&
150 | !IsExecuting;
151 | }
152 |
153 | private void ExecuteEjectCommand(object parameter)
154 | {
155 | IntPtr handle = Native.OpenProcess(ProcessAccessRights.PROCESS_ALL_ACCESS, false, SelectedAssembly.ProcessId);
156 |
157 | if (handle == IntPtr.Zero) {
158 | Status = "Failed to open process";
159 | return;
160 | }
161 |
162 | IsExecuting = true;
163 | Status = "Ejecting " + SelectedAssembly.Name;
164 |
165 | ProcessUtils.GetMonoModule(handle, out IntPtr mono);
166 |
167 | using (Injector injector = new Injector(handle, mono)) {
168 | try {
169 | injector.Eject(SelectedAssembly.Address, EjectNamespace, EjectClassName, EjectMethodName);
170 | InjectedAssemblies.Remove(SelectedAssembly);
171 | Status = "Ejection successful";
172 | } catch (InjectorException ie) {
173 | Status = "Ejection failed: " + ie.Message;
174 | } catch (Exception e) {
175 | Status = "Ejection failed (unknown error): " + e.Message;
176 | }
177 | }
178 |
179 | IsExecuting = false;
180 | }
181 |
182 | private bool _isRefreshing;
183 | public bool IsRefreshing
184 | {
185 | get => _isRefreshing;
186 | set {
187 | Set(ref _isRefreshing, value);
188 | RefreshCommand.RaiseCanExecuteChanged();
189 | }
190 | }
191 |
192 | private bool _isExecuting;
193 | public bool IsExecuting
194 | {
195 | get => _isExecuting;
196 | set {
197 | Set(ref _isExecuting, value);
198 | InjectCommand.RaiseCanExecuteChanged();
199 | EjectCommand.RaiseCanExecuteChanged();
200 | }
201 | }
202 |
203 | private ObservableCollection _processes;
204 | public ObservableCollection Processes
205 | {
206 | get => _processes;
207 | set => Set(ref _processes, value);
208 | }
209 |
210 | private MonoProcess _selectedProcess;
211 | public MonoProcess SelectedProcess
212 | {
213 | get => _selectedProcess;
214 | set {
215 | _selectedProcess = value;
216 | InjectCommand.RaiseCanExecuteChanged();
217 | }
218 | }
219 |
220 | private string _status;
221 | public string Status
222 | {
223 | get => _status;
224 | set => Set(ref _status, value);
225 | }
226 |
227 | private string _assemblyPath;
228 | public string AssemblyPath
229 | {
230 | get => _assemblyPath;
231 | set {
232 | Set(ref _assemblyPath, value);
233 | if (File.Exists(_assemblyPath))
234 | InjectNamespace = Path.GetFileNameWithoutExtension(_assemblyPath);
235 | InjectCommand.RaiseCanExecuteChanged();
236 | }
237 | }
238 |
239 | private string _injectNamespace;
240 | public string InjectNamespace
241 | {
242 | get => _injectNamespace;
243 | set {
244 | Set(ref _injectNamespace, value);
245 | EjectNamespace = value;
246 | }
247 | }
248 |
249 | private string _injectClassName;
250 | public string InjectClassName
251 | {
252 | get => _injectClassName;
253 | set {
254 | Set(ref _injectClassName, value);
255 | EjectClassName = value;
256 | InjectCommand.RaiseCanExecuteChanged();
257 | }
258 | }
259 |
260 | private string _injectMethodName;
261 | public string InjectMethodName
262 | {
263 | get => _injectMethodName;
264 | set {
265 | Set(ref _injectMethodName, value);
266 | if (_injectMethodName == "Load")
267 | EjectMethodName = "Unload";
268 | InjectCommand.RaiseCanExecuteChanged();
269 | }
270 | }
271 |
272 | private ObservableCollection _injectedAssemblies = new ObservableCollection();
273 | public ObservableCollection InjectedAssemblies
274 | {
275 | get => _injectedAssemblies;
276 | set => Set(ref _injectedAssemblies, value);
277 | }
278 |
279 | private InjectedAssembly _selectedAssembly;
280 | public InjectedAssembly SelectedAssembly
281 | {
282 | get => _selectedAssembly;
283 | set {
284 | Set(ref _selectedAssembly, value);
285 | EjectCommand.RaiseCanExecuteChanged();
286 | }
287 | }
288 |
289 | private string _ejectNamespace;
290 | public string EjectNamespace
291 | {
292 | get => _ejectNamespace;
293 | set => Set(ref _ejectNamespace, value);
294 | }
295 |
296 | private string _ejectClassName;
297 | public string EjectClassName
298 | {
299 | get => _ejectClassName;
300 | set {
301 | Set(ref _ejectClassName, value);
302 | EjectCommand.RaiseCanExecuteChanged();
303 | }
304 | }
305 |
306 | private string _ejectMethodName;
307 | public string EjectMethodName
308 | {
309 | get => _ejectMethodName;
310 | set {
311 | Set(ref _ejectMethodName, value);
312 | EjectCommand.RaiseCanExecuteChanged();
313 | }
314 | }
315 | }
316 | }
317 |
--------------------------------------------------------------------------------
/src/SharpMonoInjector.Gui/ViewModels/RelayCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Input;
3 |
4 | namespace SharpMonoInjector.Gui.ViewModels
5 | {
6 | public class RelayCommand : ICommand
7 | {
8 | public event EventHandler CanExecuteChanged;
9 |
10 | private readonly Action