Skip to content

Commit 3c45264

Browse files
committed
Files push
1 parent 6d8758c commit 3c45264

16 files changed

+821
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,4 @@ ASALocalRun/
332332

333333
# Local History for Visual Studio
334334
.localhistory/
335+
VMUP/VMUnprotect/VMUnprotect.csproj.DotSettings

VMUP/VMUP.sln

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VMUnprotect", "VMUnprotect\VMUnprotect.csproj", "{42C88429-9A67-4087-91BD-7D76383BC86D}"
4+
EndProject
5+
Global
6+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7+
Debug|Any CPU = Debug|Any CPU
8+
Release|Any CPU = Release|Any CPU
9+
EndGlobalSection
10+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
11+
{42C88429-9A67-4087-91BD-7D76383BC86D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
12+
{42C88429-9A67-4087-91BD-7D76383BC86D}.Debug|Any CPU.Build.0 = Debug|Any CPU
13+
{42C88429-9A67-4087-91BD-7D76383BC86D}.Release|Any CPU.ActiveCfg = Release|Any CPU
14+
{42C88429-9A67-4087-91BD-7D76383BC86D}.Release|Any CPU.Build.0 = Release|Any CPU
15+
EndGlobalSection
16+
EndGlobal
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using HarmonyLib;
2+
using System;
3+
using System.Reflection;
4+
using VMUnprotect.Hooks.Methods;
5+
using VMUnprotect.Utils;
6+
7+
namespace VMUnprotect.Hooks
8+
{
9+
/// <summary>
10+
/// Manages patches of Harmony
11+
/// </summary>
12+
public static class HooksManager
13+
{
14+
// Harmony instance
15+
private static Harmony Harmony { get; set; }
16+
17+
/// <summary>
18+
/// Applies Harmony patches to VMP functions.
19+
/// </summary>
20+
/// <param name="runtimeStructure">Structure of VMP Runtime</param>
21+
internal static void HooksApply(VmRuntimeStructure runtimeStructure)
22+
{
23+
// Enable Harmony Debug file.
24+
Harmony.DEBUG = true;
25+
26+
Harmony = new Harmony("com.hussaryyn.vmup");
27+
Harmony.PatchAll(typeof(Loader).Assembly);
28+
29+
// Check if functionHandler was found.
30+
if (runtimeStructure.FunctionHandler is null)
31+
throw new ArgumentException("Could not locate Function Handler.");
32+
33+
// resolve method.
34+
var mdToken = runtimeStructure.FunctionHandler.MDToken.ToInt32();
35+
var resolvedVmpHandler = Loader.VmpAssembly.ManifestModule.ResolveMethod(mdToken);
36+
37+
ConsoleLogger.Debug("Found VMPFunctionHandler, MDToken {0}", mdToken);
38+
ConsoleLogger.Debug("Applying Transpiler and replacing Invokes.");
39+
40+
// Apply transpiler to the function handler.
41+
Harmony.Patch(resolvedVmpHandler, transpiler: new HarmonyMethod(typeof(VmProtectDumper).GetMethod(nameof(VmProtectDumper.Transpiler), (BindingFlags) (-1))));
42+
}
43+
}
44+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using HarmonyLib;
2+
using System.Reflection;
3+
using VMUnprotect.Utils;
4+
5+
namespace VMUnprotect.Hooks.Methods
6+
{
7+
/// <summary>
8+
/// Harmony Patch for GetCallingAssembly
9+
/// </summary>
10+
[HarmonyPatch(typeof(Assembly))]
11+
[HarmonyPatch("GetCallingAssembly")]
12+
public class GetCallingAssemblyPatch
13+
{
14+
public static void Postfix(ref Assembly __result)
15+
{
16+
if (__result != typeof(GetCallingAssemblyPatch).Assembly) return;
17+
18+
ConsoleLogger.Debug("Swapped [{0}] '{1}'", "GetCallingAssembly", __result.FullName);
19+
__result = Loader.VmpAssembly;
20+
}
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using HarmonyLib;
2+
using System.Reflection;
3+
using VMUnprotect.Utils;
4+
5+
namespace VMUnprotect.Hooks.Methods
6+
{
7+
/// <summary>
8+
/// Harmony Patch for GetEntryAssembly
9+
/// </summary>
10+
[HarmonyPatch(typeof(Assembly))]
11+
[HarmonyPatch("GetEntryAssembly")]
12+
public class GetEntryAssemblyPatch
13+
{
14+
public static void Postfix(ref Assembly __result)
15+
{
16+
if (__result != typeof(GetEntryAssemblyPatch).Assembly) return;
17+
18+
ConsoleLogger.Debug("Swapped [{0}] '{1}'", "GetEntryAssembly", __result.FullName);
19+
__result = Loader.VmpAssembly;
20+
}
21+
}
22+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
using HarmonyLib;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Globalization;
5+
using System.Linq;
6+
using System.Reflection;
7+
using System.Reflection.Emit;
8+
using VMUnprotect.Utils;
9+
10+
namespace VMUnprotect.Hooks.Methods
11+
{
12+
/// <summary>
13+
/// This class holds target functions.
14+
/// </summary>
15+
internal class Targets
16+
{
17+
/// <summary>
18+
/// This function is used by current VMProtect version.
19+
/// </summary>
20+
/// <param name="obj">
21+
/// The object on which to invoke the method or constructor. If a method is static, this argument is
22+
/// ignored. If a constructor is static, this argument must be <see langword="null" /> or an instance of the class that
23+
/// defines the constructor.
24+
/// </param>
25+
/// <param name="invokeAttr">
26+
/// A bitmask that is a combination of 0 or more bit flags from
27+
/// <see cref="T:System.Reflection.BindingFlags" />. If <paramref name="binder" /> is <see langword="null" />, this
28+
/// parameter is assigned the value <see cref="F:System.Reflection.BindingFlags.Default" />; thus, whatever you pass in
29+
/// is ignored.
30+
/// </param>
31+
/// <param name="binder">
32+
/// An object that enables the binding, coercion of argument types, invocation of members, and
33+
/// retrieval of <see langword="MemberInfo" /> objects via reflection. If <paramref name="binder" /> is
34+
/// <see langword="null" />, the default binder is used.
35+
/// </param>
36+
/// <param name="parameters">
37+
/// An argument list for the invoked method or constructor. This is an array of objects with the same number, order,
38+
/// and type as the parameters of the method or constructor to be invoked. If there are no parameters, this should be
39+
/// <see langword="null" />.
40+
/// If the method or constructor represented by this instance takes a ByRef parameter, there is no special attribute
41+
/// required for that parameter in order to invoke the method or constructor using this function. Any object in this
42+
/// array that is not explicitly initialized with a value will contain the default value for that object type. For
43+
/// reference-type elements, this value is <see langword="null" />. For value-type elements, this value is 0, 0.0, or
44+
/// <see langword="false" />, depending on the specific element type.
45+
/// </param>
46+
/// <param name="culture">
47+
/// An instance of <see langword="CultureInfo" /> used to govern the coercion of types. If this is
48+
/// <see langword="null" />, the <see langword="CultureInfo" /> for the current thread is used. (This is necessary to
49+
/// convert a <see langword="String" /> that represents 1000 to a <see langword="Double" /> value, for example, since
50+
/// 1000 is represented differently by different cultures.)
51+
/// </param>
52+
/// <returns>
53+
/// An <see langword="Object" /> containing the return value of the invoked method, or <see langword="null" /> in
54+
/// the case of a constructor, or <see langword="null" /> if the method's return type is <see langword="void" />.
55+
/// Before calling the method or constructor, <see langword="Invoke" /> checks to see if the user has access permission
56+
/// and verifies that the parameters are valid.
57+
/// </returns>
58+
public object HookedInvoke(object obj, BindingFlags bindingFlags, Binder binder, object[] parameters, CultureInfo culture, MethodBase methodBase)
59+
{
60+
try
61+
{
62+
// Indicate this method was called by newer version of VMP.
63+
ConsoleLogger.Warn("============================================= HookedInvoke =============================================\n");
64+
65+
// Invoke the method and get return value.
66+
var returnValue = methodBase.Invoke(obj, bindingFlags, binder, parameters, culture);
67+
68+
// Route the arguments and return value to our middleman function where they can be manipulated or logged.
69+
MiddleMan.VmpMethodLogger(obj, bindingFlags, binder, ref parameters, culture, methodBase, ref returnValue);
70+
71+
// Return logged return value.
72+
return returnValue;
73+
}
74+
catch (Exception ex)
75+
{
76+
// Log the exception.
77+
ConsoleLogger.Error(ex.StackTrace);
78+
return null;
79+
}
80+
}
81+
82+
/// <summary>
83+
/// This function is used by older VMProtect version.
84+
/// </summary>
85+
/// <param name="obj">
86+
/// The object on which to invoke the method or constructor. If a method is static, this argument is
87+
/// ignored. If a constructor is static, this argument must be <see langword="null" /> or an instance of the class that
88+
/// defines the constructor.
89+
/// </param>
90+
/// <param name="parameters">
91+
/// An argument list for the invoked method or constructor. This is an array of objects with the same number, order,
92+
/// and type as the parameters of the method or constructor to be invoked. If there are no parameters,
93+
/// <paramref name="parameters" /> should be <see langword="null" />.
94+
/// If the method or constructor represented by this instance takes a <see langword="ref" /> parameter (
95+
/// <see langword="ByRef" /> in Visual Basic), no special attribute is required for that parameter in order to invoke
96+
/// the method or constructor using this function. Any object in this array that is not explicitly initialized with a
97+
/// value will contain the default value for that object type. For reference-type elements, this value is
98+
/// <see langword="null" />. For value-type elements, this value is 0, 0.0, or <see langword="false" />, depending on
99+
/// the specific element type.
100+
/// </param>
101+
/// <returns>
102+
/// An object containing the return value of the invoked method, or <see langword="null" /> in the case of a
103+
/// constructor.
104+
/// </returns>
105+
public object HookedInvokeOld(object obj, object[] parameters, MethodBase methodBase)
106+
{
107+
try
108+
{
109+
// Indicate this method was called by older version of VMP.
110+
ConsoleLogger.Warn("============================================= HookedInvokeOld =============================================\n");
111+
112+
// Invoke the method and get return value.
113+
var returnValue = methodBase.Invoke(obj, parameters);
114+
115+
// Route the arguments and return value to our middleman function where they can be manipulated or logged.
116+
MiddleMan.VmpMethodLogger(obj, null, null, ref parameters, null, methodBase, ref returnValue);
117+
118+
// Return logged return value.
119+
return returnValue;
120+
}
121+
catch (Exception ex)
122+
{
123+
// Log the exception.
124+
ConsoleLogger.Error(ex.StackTrace);
125+
return null;
126+
}
127+
}
128+
}
129+
130+
/// <summary>
131+
/// This class contains Harmony Patches
132+
/// </summary>
133+
public static class VmProtectDumper
134+
{
135+
/// <summary>A transpiler that replaces all occurrences of a given method with another with additional Ldarg_1 instruction</summary>
136+
/// <param name="instructions">The enumeration of <see cref="T:HarmonyLib.CodeInstruction" /> to act on</param>
137+
/// <param name="from">Method to search for</param>
138+
/// <param name="to">Method to replace with</param>
139+
/// <returns>Modified enumeration of <see cref="T:HarmonyLib.CodeInstruction" /></returns>
140+
private static IEnumerable<CodeInstruction> ReplaceVmpInvoke(this IEnumerable<CodeInstruction> instructions, MethodBase from, MethodBase to)
141+
{
142+
if ((object) from == null) throw new ArgumentException("Unexpected null argument", nameof(from));
143+
if ((object) to == null) throw new ArgumentException("Unexpected null argument", nameof(to));
144+
145+
var code = new List<CodeInstruction>(instructions);
146+
147+
for (var x = 0; x < code.Count; x++)
148+
{
149+
var ins = code[x];
150+
if (ins.operand as MethodBase != from) continue;
151+
152+
// replace callvirt Invoke with our debug invoke.
153+
ins.opcode = OpCodes.Callvirt;
154+
ins.operand = to;
155+
156+
// insert additional Ldarg_1 which corresponds to MethodBase of invoked function.
157+
// TODO: Improve this, can be easily broken by obfuscation or future VMP updates
158+
code.Insert(x, new CodeInstruction(OpCodes.Ldarg_1));
159+
ConsoleLogger.Info("Replaced with custom Invoke and injected MethodBase argument at {0}.", x);
160+
}
161+
162+
return code.AsEnumerable();
163+
}
164+
165+
/// <summary>A transpiler that alters instructions that calls specific method</summary>
166+
/// <param name="instructions">The enumeration of <see cref="T:HarmonyLib.CodeInstruction" /> to act on</param>
167+
/// <returns>Modified enumeration of <see cref="T:HarmonyLib.CodeInstruction" /></returns>
168+
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
169+
{
170+
ConsoleLogger.Debug("VMP Function Handler Transpiler");
171+
172+
var codeInstructions = instructions.ToList();
173+
174+
// Replace all occurrences of MethodBase.Invoke with our debug version.
175+
return codeInstructions.ReplaceVmpInvoke(AccessTools.Method(typeof(MethodBase),
176+
"Invoke",
177+
new[]
178+
{
179+
typeof(object), typeof(BindingFlags), typeof(Binder), typeof(object[]),
180+
typeof(CultureInfo)
181+
}),
182+
AccessTools.Method(typeof(Targets), nameof(Targets.HookedInvoke)))
183+
// Older version of VMP
184+
.ReplaceVmpInvoke(AccessTools.Method(typeof(MethodBase), "Invoke", new[] {typeof(object), typeof(object[])}),
185+
AccessTools.Method(typeof(Targets), nameof(Targets.HookedInvokeOld)));
186+
}
187+
}
188+
}

VMUP/VMUnprotect/Init/Loader.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using dnlib.DotNet;
2+
using HarmonyLib;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Reflection;
7+
using System.Runtime.CompilerServices;
8+
using VMUnprotect.Hooks;
9+
using VMUnprotect.Utils;
10+
11+
namespace VMUnprotect
12+
{
13+
public class Loader
14+
{
15+
public Loader(string path)
16+
{
17+
VmpAssembly = TryLoadAssembly(path);
18+
VmpModuleDefMd = TryLoadModuleDef(path);
19+
}
20+
21+
internal static Assembly VmpAssembly
22+
{
23+
get;
24+
set;
25+
}
26+
internal static ModuleDefMD VmpModuleDefMd
27+
{
28+
get;
29+
set;
30+
}
31+
32+
public VmRuntimeStructure RuntimeStructure
33+
{
34+
get;
35+
set;
36+
}
37+
38+
public void Start(IEnumerable<string> args)
39+
{
40+
var fileEntryPoint = VmpAssembly.EntryPoint;
41+
var moduleHandle = VmpAssembly.ManifestModule.ModuleHandle;
42+
var parameters = fileEntryPoint.GetParameters();
43+
var arguments = args.Skip(1).ToArray();
44+
45+
ConsoleLogger.Debug("Entrypoint method: {0}", fileEntryPoint.FullDescription());
46+
ConsoleLogger.Debug("ModuleHandle: {0}", moduleHandle);
47+
48+
ConsoleLogger.Debug("Analyzing VMP Structure...");
49+
VmAnalyzer.Run(this);
50+
51+
ConsoleLogger.Info("Applying hooks.");
52+
HooksManager.HooksApply(RuntimeStructure);
53+
54+
ConsoleLogger.Info("Invoking Constructor.");
55+
RuntimeHelpers.RunModuleConstructor(moduleHandle);
56+
57+
ConsoleLogger.Info("Invoking assembly.");
58+
fileEntryPoint.Invoke(null, parameters.Length == 0 ? null : new object[] {arguments});
59+
}
60+
61+
private static Assembly TryLoadAssembly(string path)
62+
{
63+
try
64+
{
65+
return Assembly.LoadFile(path);
66+
}
67+
catch (Exception ex)
68+
{
69+
Console.WriteLine(ex.Message);
70+
return null;
71+
}
72+
}
73+
74+
private static ModuleDefMD TryLoadModuleDef(string path)
75+
{
76+
try
77+
{
78+
return ModuleDefMD.Load(path);
79+
}
80+
catch (Exception ex)
81+
{
82+
Console.WriteLine(ex.Message);
83+
return null;
84+
}
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)