Skip to content

Commit 3ad8c76

Browse files
committed
Code cleanup and restructure
1 parent b92f496 commit 3ad8c76

35 files changed

+596
-561
lines changed

README.md

Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<p align="center">
2-
<img width="256" heigth="256" src="Images/vmup.png">
2+
<img width="256" heigth="256" src="Docs/vmup.png">
33
<h1 align="center">VMUnprotect.NET</h1>
44
<p align="center">
55
<strong>VMUnprotect</strong> is a project engaged in hunting virtualized <a href="https://vmpsoft.com">VMProtect</a> methods. It makes use of <a href="https://github.com/pardeike/Harmony">Harmony</a> to dynamically read <strong>VMP</strong> behavior. Currently only supports method administration. Works on <a href="https://vmpsoft.com/20210919/vmprotect-3-5-1/">VMProtect 3.5.1</a> (Latest) and few versions back.
@@ -12,15 +12,16 @@
1212
</p>
1313

1414
## Showcase
15-
<img src="Images/show.gif">
15+
<img src="Docs/show.gif">
1616

1717
# Usage
1818
```sh
1919
VMUnprotect.exe
20-
-f, --file Required. Path to file.
21-
--usetranspiler (Default: false) Use an older method that makes use of Transpiler (not recommended).
22-
--help Display this help screen.
23-
--version Display version information.
20+
-f, --file Required. Path to file.
21+
--usetranspiler (Default: false) Use an older method that makes use of Transpiler (not recommended).
22+
--enableharmonylogs (Default: true) Enable logs from Harmony.
23+
--help Display this help screen.
24+
--version Display version information.
2425
```
2526

2627
# Supported Protections
@@ -36,36 +37,53 @@ Virtualization Tools | Yes
3637
Strip Debug Information | Yes
3738
Pack the Output File | No
3839

39-
# Usage can be found in ```Methods\MiddleMan.cs```
40+
# Usage can be found in ```MiddleMan```
4041
```csharp
41-
internal static class MiddleMan
42-
{
43-
public static void Prefix(ref object __instance, ref object obj, ref object[] parameters, ref object[] arguments)
44-
{
45-
var virtualizedMethodName = new StackTrace().GetFrame(7).GetMethod();
46-
var method = (MethodBase) __instance;
47-
48-
ConsoleLogger.Warn("\tVMP MethodName: {0} (MDToken {1:X4})", virtualizedMethodName.FullDescription(), virtualizedMethodName.MetadataToken.ToString());
49-
50-
ConsoleLogger.Warn("\tMethodName: {0}", method.Name);
51-
ConsoleLogger.Warn("\tFullDescription: {0}", method.FullDescription());
52-
ConsoleLogger.Warn("\tMethodType: {0}", method.GetType());
53-
if (obj != null) ConsoleLogger.Warn("\nObj: {0}", obj.GetType());
54-
55-
// Loop through parameters and log them
56-
for (var i = 0; i < parameters.Length; i++)
57-
{
58-
var parameter = parameters[i] ?? "null";
59-
ConsoleLogger.Warn("\tParameter ({1}) [{0}]: ({2})", i, parameter.GetType(), parameter);
42+
using HarmonyLib;
43+
using System.Diagnostics;
44+
using System.Reflection;
45+
using VMUnprotect.Core.Abstraction;
46+
using VMUnprotect.Core.Helpers;
47+
48+
namespace VMUnprotect.Core.MiddleMan {
49+
/// <summary>
50+
/// Works as Middle Man to make life easier
51+
/// </summary>
52+
public static class UnsafeInvokeMiddleMan {
53+
private static readonly ILogger ConsoleLogger = Engine.Logger;
54+
55+
/// <summary>
56+
/// A prefix is a method that is executed before the original method
57+
/// </summary>
58+
public static void Prefix(ref object __instance, ref object obj, ref object[] parameters, ref object[] arguments) {
59+
var virtualizedMethodName = new StackTrace().GetFrame(7).GetMethod();
60+
var method = (MethodBase) __instance;
61+
62+
ConsoleLogger.Print("VMP MethodName: {0} (MDToken {1:X4})", virtualizedMethodName.FullDescription(), virtualizedMethodName.MetadataToken.ToString());
63+
ConsoleLogger.Print("MethodName: {0}", method.Name);
64+
ConsoleLogger.Print("FullDescription: {0}", method.FullDescription());
65+
ConsoleLogger.Print("MethodType: {0}", method.GetType());
66+
67+
if (obj is not null)
68+
ConsoleLogger.Print("Obj: {0}", obj.GetType());
69+
70+
// Loop through parameters and log them
71+
for (var i = 0; i < parameters.Length; i++) {
72+
var parameter = parameters[i];
73+
ConsoleLogger.Print("Parameter ({1}) [{0}]: ({2})", i, parameter.GetType(), Formatter.FormatObject(parameter));
74+
}
75+
76+
var returnType = method is MethodInfo info ? info.ReturnType.FullName : "System.Object";
77+
ConsoleLogger.Print("MDToken: 0x{0:X4}", method.MetadataToken);
78+
ConsoleLogger.Print("Return Type: {0}", returnType ?? "null");
79+
}
80+
81+
/// <summary>
82+
/// A postfix is a method that is executed after the original method
83+
/// </summary>
84+
public static void Postfix(ref object __instance, ref object __result, ref object obj, ref object[] parameters, ref object[] arguments) {
85+
ConsoleLogger.Print("Returns: {0}", __result);
6086
}
61-
var returnType = method is MethodInfo info ? info.ReturnType.FullName : "System.Object";
62-
ConsoleLogger.Warn("\tMDToken: 0x{0:X4}", method.MetadataToken);
63-
ConsoleLogger.Warn("\tReturn Type: {0}", returnType);
64-
}
65-
66-
public static void Postfix(ref object __instance, ref object __result, ref object obj, ref object[] parameters, ref object[] arguments)
67-
{
68-
ConsoleLogger.Warn("\tReturns: {0}", __result);
6987
}
7088
}
7189
```

VMUP/VMUP.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Microsoft Visual Studio Solution File, Format Version 12.00
33
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VMUnprotect", "VMUnprotect\VMUnprotect.csproj", "{42C88429-9A67-4087-91BD-7D76383BC86D}"
44
EndProject
5+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VMUnprotect.Core", "VMUnprotect.Core\VMUnprotect.Core.csproj", "{D57B9692-671D-450B-B21C-24BA0129B888}"
6+
EndProject
57
Global
68
GlobalSection(SolutionConfigurationPlatforms) = preSolution
79
Debug|Any CPU = Debug|Any CPU
@@ -12,5 +14,9 @@ Global
1214
{42C88429-9A67-4087-91BD-7D76383BC86D}.Debug|Any CPU.Build.0 = Debug|Any CPU
1315
{42C88429-9A67-4087-91BD-7D76383BC86D}.Release|Any CPU.ActiveCfg = Release|Any CPU
1416
{42C88429-9A67-4087-91BD-7D76383BC86D}.Release|Any CPU.Build.0 = Release|Any CPU
17+
{D57B9692-671D-450B-B21C-24BA0129B888}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18+
{D57B9692-671D-450B-B21C-24BA0129B888}.Debug|Any CPU.Build.0 = Debug|Any CPU
19+
{D57B9692-671D-450B-B21C-24BA0129B888}.Release|Any CPU.ActiveCfg = Release|Any CPU
20+
{D57B9692-671D-450B-B21C-24BA0129B888}.Release|Any CPU.Build.0 = Release|Any CPU
1521
EndGlobalSection
1622
EndGlobal
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
3+
namespace VMUnprotect.Core.Abstraction {
4+
public class EmptyLogger : ILogger {
5+
public void Debug(string m, params object[] f) {
6+
throw new NotImplementedException();
7+
}
8+
public void Error(string m, params object[] f) {
9+
throw new NotImplementedException();
10+
}
11+
public void Info(string m, params object[] f) {
12+
throw new NotImplementedException();
13+
}
14+
public void Warn(string m, params object[] f) {
15+
throw new NotImplementedException();
16+
}
17+
public void Print(string m, params object[] f) {
18+
throw new NotImplementedException();
19+
}
20+
}
21+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace VMUnprotect.Core.Abstraction {
2+
public interface ILogger {
3+
public void Debug(string m, params object[] f);
4+
public void Error(string m, params object[] f);
5+
public void Info(string m, params object[] f);
6+
public void Warn(string m, params object[] f);
7+
public void Print(string m, params object[] f);
8+
}
9+
}
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
using CommandLine;
22

3-
namespace VMUnprotect
4-
{
5-
public class CommandLineOptions
6-
{
3+
namespace VMUnprotect.Core {
4+
public class CommandLineOptions {
75
[Option('f', "file", HelpText = "Path to file.", Required = true)]
86
public string FilePath { get; set; }
97

108
[Option(Default = false, HelpText = "Use an older method that makes use of Transpiler (not recommended).", Required = false)]
119
public bool UseTranspiler { get; set; }
10+
11+
[Option(Default = true, HelpText = "Enable logs from Harmony.", Required = false)]
12+
public bool EnableHarmonyLogs { get; set; }
1213
}
1314
}

VMUP/VMUnprotect.Core/Context.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using dnlib.DotNet;
2+
using HarmonyLib;
3+
using System;
4+
using System.Reflection;
5+
using VMUnprotect.Core.Structure;
6+
using ILogger = VMUnprotect.Core.Abstraction.ILogger;
7+
8+
namespace VMUnprotect.Core {
9+
public class Context {
10+
public Context(ILogger logger, string vmpAssembly, CommandLineOptions options) {
11+
Harmony.DEBUG = options.EnableHarmonyLogs;
12+
13+
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
14+
Options = options ?? throw new ArgumentNullException(nameof(options));
15+
16+
VmpAssembly = Assembly.LoadFile(vmpAssembly);
17+
VmpModuleDefMd = ModuleDefMD.Load(vmpAssembly);
18+
19+
Harmony = new Harmony("com.hussaryyn.vmup");
20+
}
21+
22+
public ILogger Logger { get; }
23+
public Assembly VmpAssembly { get; }
24+
public ModuleDefMD VmpModuleDefMd { get; }
25+
public VmRuntimeStructure? RuntimeStructure { get; set; } = new();
26+
public Harmony Harmony { get; }
27+
public CommandLineOptions Options { get; }
28+
}
29+
}

VMUP/VMUnprotect.Core/Engine.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using HarmonyLib;
2+
using System;
3+
using System.Runtime.CompilerServices;
4+
using VMUnprotect.Core.Abstraction;
5+
using VMUnprotect.Core.Hooks;
6+
using VMUnprotect.Core.Structure;
7+
8+
namespace VMUnprotect.Core {
9+
public class Engine {
10+
public Engine(Context context) {
11+
Ctx = context;
12+
Logger = context.Logger;
13+
}
14+
internal static Context Ctx
15+
{
16+
get;
17+
private set;
18+
} = null!;
19+
20+
public static ILogger Logger
21+
{
22+
get;
23+
private set;
24+
} = new EmptyLogger();
25+
26+
public void Start() {
27+
var fileEntryPoint = Ctx.VmpAssembly.EntryPoint;
28+
var moduleHandle = Ctx.VmpAssembly.ManifestModule.ModuleHandle;
29+
var parameters = fileEntryPoint.GetParameters();
30+
31+
Logger.Debug("Entrypoint method: {0}", fileEntryPoint.FullDescription());
32+
Logger.Debug("ModuleHandle: {0:X4}", moduleHandle);
33+
34+
Logger.Debug("--- Analyzing VMP Structure...");
35+
AnalyzeStructure();
36+
37+
Logger.Info("--- Applying hooks.");
38+
ApplyHooks();
39+
40+
Logger.Info("--- Invoking Constructor.");
41+
RuntimeHelpers.RunModuleConstructor(moduleHandle);
42+
43+
Logger.Info("--- Invoking assembly.\n");
44+
fileEntryPoint.Invoke(null, parameters.Length == 0 ? null : new object[] {null!});
45+
}
46+
private static void ApplyHooks() {
47+
try {
48+
HooksManager.HooksApply(Ctx);
49+
}
50+
catch (Exception ex) {
51+
Logger.Error("Failed to apply Harmony patches: {0}", ex.StackTrace);
52+
}
53+
}
54+
55+
private void AnalyzeStructure() {
56+
try {
57+
VmAnalyzer.Run(Ctx);
58+
}
59+
catch (Exception ex) {
60+
Logger.Error("Failed to analyze VMProtect Structure: {0}", ex.StackTrace);
61+
}
62+
}
63+
}
64+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
using System.Collections;
3+
using System.Linq;
4+
5+
namespace VMUnprotect.Core.Helpers {
6+
public static class Formatter {
7+
/// <summary>
8+
/// Formats code to more reliable format
9+
/// </summary>
10+
public static string FormatObject(object obj) {
11+
try {
12+
return obj switch {
13+
null => "null",
14+
string x => $"\"{x}\"",
15+
IEnumerable enumerable => $"{obj.GetType().Name} {{{string.Join(", ", enumerable.Cast<object>().Select(FormatObject))}}}",
16+
_ => obj.ToString()
17+
};
18+
}
19+
catch (Exception) {
20+
return "???";
21+
}
22+
}
23+
}
24+
}

VMUP/VMUnprotect/Utils/StringCounts.cs renamed to VMUP/VMUnprotect.Core/Helpers/StringCounts.cs

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,63 +4,51 @@
44
using System.Collections.Generic;
55
using System.Linq;
66

7-
namespace VMUnprotect.Utils
8-
{
7+
namespace VMUnprotect.Core.Helpers {
98
/// <summary>
109
/// This code belongs to
1110
/// https://github.com/de4dot/de4dot/blob/b7d5728fc0c82fb0ad758e3a4c0fbb70368a4853/de4dot.code/deobfuscators/StringCounts.cs
1211
/// </summary>
13-
public class StringCounts
14-
{
12+
public class StringCounts {
1513
private readonly Dictionary<string, int> _strings = new(StringComparer.Ordinal);
1614

1715
public IEnumerable<string> Strings => _strings.Keys;
1816
public int NumStrings => _strings.Count;
1917

20-
protected void Add(string s)
21-
{
18+
protected void Add(string s) {
2219
_strings.TryGetValue(s, out var count);
2320
_strings[s] = count + 1;
2421
}
2522

26-
private bool Exists(string s)
27-
{
28-
return s != null && _strings.ContainsKey(s);
23+
private bool Exists(string s) {
24+
return _strings.ContainsKey(s);
2925
}
3026

31-
public bool All(IEnumerable<string> list)
32-
{
27+
public bool All(IEnumerable<string> list) {
3328
return list.All(Exists);
3429
}
3530

36-
public bool Exactly(ICollection<string> list)
37-
{
31+
public bool Exactly(ICollection<string> list) {
3832
return list.Count == _strings.Count && All(list);
3933
}
4034

41-
public int Count(string s)
42-
{
35+
public int Count(string s) {
4336
_strings.TryGetValue(s, out var count);
4437
return count;
4538
}
4639
}
4740

4841

49-
public class LocalTypes : StringCounts
50-
{
51-
public LocalTypes(MethodDef method)
52-
{
42+
public class LocalTypes : StringCounts {
43+
public LocalTypes(MethodDef method) {
5344
if (method is {Body: { }}) Initialize(method.Body.Variables);
5445
}
5546

56-
public LocalTypes(IEnumerable<Local> locals)
57-
{
47+
public LocalTypes(IEnumerable<Local> locals) {
5848
Initialize(locals);
5949
}
6050

61-
private void Initialize(IEnumerable<Local> locals)
62-
{
63-
if (locals == null) return;
51+
private void Initialize(IEnumerable<Local> locals) {
6452
foreach (var local in locals) Add(local.Type.FullName);
6553
}
6654
}

0 commit comments

Comments
 (0)