aboutsummaryrefslogtreecommitdiff
path: root/runtime/VMProtect.Runtime/Core.cs
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/VMProtect.Runtime/Core.cs')
-rw-r--r--runtime/VMProtect.Runtime/Core.cs526
1 files changed, 526 insertions, 0 deletions
diff --git a/runtime/VMProtect.Runtime/Core.cs b/runtime/VMProtect.Runtime/Core.cs
new file mode 100644
index 0000000..97bb3d2
--- /dev/null
+++ b/runtime/VMProtect.Runtime/Core.cs
@@ -0,0 +1,526 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Diagnostics;
+using System.Threading;
+using System.Collections.Generic;
+using System.Text;
+
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable UnusedParameter.Global
+// ReSharper disable UnusedMember.Global
+// ReSharper disable once CheckNamespace
+[assembly: SuppressIldasmAttribute()]
+[assembly: Obfuscation(Feature = "renaming", Exclude = true)]
+namespace VMProtect
+{
+ /// <summary>
+ /// Serial number status flags
+ /// </summary>
+ [Flags]
+ public enum SerialState
+ {
+ /// <summary>
+ /// No problems.
+ /// </summary>
+ Success = 0x00000000,
+ /// <summary>
+ /// Licensing module is corrupted. This may be caused by cracking attempts. Usually you will never get this flag.
+ /// </summary>
+ Corrupted = 0x00000001,
+ /// <summary>
+ /// Serial number is invalid. You will get this flag if licensing module will be unable to decrypt serial number or
+ /// if the serial number is not yet set or it is empty.
+ /// </summary>
+ Invalid = 0x00000002,
+ /// <summary>
+ /// Serial number is correct, but it was blacklisted in VMProtect.
+ /// </summary>
+ Blacklisted = 0x00000004,
+ /// <summary>
+ /// Serial number is expired. You will get this flag if serial number has expiration date chunk and this date is in the past.
+ /// You may read the expiration date by calling GetSerialNumberData() function.
+ /// </summary>
+ DateExpired = 0x00000008,
+ /// <summary>
+ /// Running time limit for this session is over. You may read limit value by calling GetSerialNumberData() function.
+ /// </summary>
+ RunningTimeOver = 0x00000010,
+ /// <summary>
+ /// Serial number is locked to another hardware identifier.
+ /// </summary>
+ BadHwid = 0x00000020,
+ /// <summary>
+ /// Serial number will not work with this version of application, because it has "Max Build Date" block and the application was
+ /// build after those date. You may read maximal build date by calling GetSerialNumberData() function.
+ /// </summary>
+ MaxBuildExpired = 0x00000040,
+ };
+
+ /// <summary>
+ /// Serial number contents
+ /// </summary>
+ public class SerialNumberData
+ {
+ /// <summary>
+ /// Serial number status
+ /// </summary>
+ public SerialState State;
+
+ /// <summary>
+ /// End user name
+ /// </summary>
+ public string UserName;
+
+ /// <summary>
+ /// End user E-Mail
+ /// </summary>
+ public string EMail;
+
+ /// <summary>
+ /// Serial number expiration date.
+ /// </summary>
+ public DateTime Expires;
+
+ /// <summary>
+ /// Max date of build, that will accept this key
+ /// </summary>
+ public DateTime MaxBuild;
+
+ /// <summary>
+ /// Running time in minutes
+ /// </summary>
+ public int RunningTime;
+
+ /// <summary>
+ /// Up to 255 bytes of user data
+ /// </summary>
+ public byte[] UserData;
+
+ public SerialNumberData()
+ {
+ State = SerialState.Invalid;
+ Expires = DateTime.MaxValue;
+ MaxBuild = DateTime.MaxValue;
+ RunningTime = 0;
+ UserData = new byte[0];
+ UserName = "";
+ EMail = "";
+ }
+ };
+
+ /// <summary>
+ /// Activation API status codes
+ /// </summary>
+ public enum ActivationStatus
+ {
+ /// <summary>
+ /// Activation successful, the serial field contains a serial number.
+ /// </summary>
+ Ok,
+ /// <summary>
+ /// The activation module was unable to connect the Web License Manager.
+ /// </summary>
+ NoConnection,
+ /// <summary>
+ /// The server returned unexpected reply. This means configuration problems or probably a cracking attempt.
+ /// </summary>
+ BadReply,
+ /// <summary>
+ /// The activation code is blocked on the server. This doesn't mean that the number of possible activations is exceeded,
+ /// this means that this exactly code is banned by the vendor, so the user is trying to cheat you.
+ /// </summary>
+ Banned,
+ /// <summary>
+ /// Something goes really wrong here. The error means that the internal activation stuff is not working,
+ /// it is not safe to continue with the activation, registration and so on.
+ /// </summary>
+ Corrupted,
+ /// <summary>
+ /// The code has been successfully passed to the server and it was unable to find it in the database.
+ /// Probably the user made a mistake, so it is worth asking the user to check the entered code for mistakes.
+ /// </summary>
+ BadCode,
+ /// <summary>
+ /// The code has been used too many times and cannot be used more. This doesn't mean the code is bad or banned, it is OK.
+ /// The user may contact vendor to ask/purchase more activations, or the user may deactivate some installations to increase
+ /// the number of activations available for that code.
+ /// </summary>
+ AlreadyUsed,
+ /// <summary>
+ /// This is the deactivation error code that means that the server can't find the serial number that the user tries to deactivate.
+ /// </summary>
+ SerialUnknown,
+ /// <summary>
+ /// The code is expired.
+ /// </summary>
+ Expired,
+ /// <summary>
+ /// Activation/deactivation features are not available.
+ /// </summary>
+ NotAvailable
+ };
+
+ [Flags]
+ [VMProtect.DeleteOnCompilation]
+ enum CoreOption
+ {
+ MEMORY_PROTECTION = 0x1,
+ CHECK_DEBUGGER = 0x2
+ }
+
+ public class Core
+ {
+ public static bool IsProtected() { return true; }
+ public static bool IsDebuggerPresent(bool checkKernelMode)
+ {
+ if (GlobalData.IsDebuggerDetected())
+ return true;
+
+ if (Debugger.IsAttached || Debugger.IsLogging())
+ return true;
+
+ if (Win32.IsDebuggerPresent() || Win32.CheckRemoteDebuggerPresent())
+ return true;
+
+ return false;
+ }
+
+ internal static bool FindFirmwareVendor(byte[] data)
+ {
+ for (var i = 0; i < data.Length; i++)
+ {
+ if (i + 3 < data.Length && data[i + 0] == 'Q' && data[i + 1] == 'E' && data[i + 2] == 'M' && data[i + 3] == 'U')
+ return true;
+ if (i + 8 < data.Length && data[i + 0] == 'M' && data[i + 1] == 'i' && data[i + 2] == 'c' && data[i + 3] == 'r' && data[i + 4] == 'o' && data[i + 5] == 's' && data[i + 6] == 'o' && data[i + 7] == 'f' && data[i + 8] == 't')
+ return true;
+ if (i + 6 < data.Length && data[i + 0] == 'i' && data[i + 1] == 'n' && data[i + 2] == 'n' && data[i + 3] == 'o' && data[i + 4] == 't' && data[i + 5] == 'e' && data[i + 6] == 'k')
+ return true;
+ if (i + 9 < data.Length && data[i + 0] == 'V' && data[i + 1] == 'i' && data[i + 2] == 'r' && data[i + 3] == 't' && data[i + 4] == 'u' && data[i + 5] == 'a' && data[i + 6] == 'l' && data[i + 7] == 'B' && data[i + 8] == 'o' && data[i + 9] == 'x')
+ return true;
+ if (i + 5 < data.Length && data[i + 0] == 'V' && data[i + 1] == 'M' && data[i + 2] == 'w' && data[i + 3] == 'a' && data[i + 4] == 'r' && data[i + 5] == 'e')
+ return true;
+ if (i + 8 < data.Length && data[i + 0] == 'P' && data[i + 1] == 'a' && data[i + 2] == 'r' && data[i + 3] == 'a' && data[i + 4] == 'l' && data[i + 5] == 'l' && data[i + 6] == 'e' && data[i + 7] == 'l' && data[i + 8] == 's')
+ return true;
+ }
+ return false;
+ }
+
+ public static bool IsVirtualMachinePresent()
+ {
+ var info = CpuId.Invoke(1);
+ if (((info[2] >> 31) & 1) != 0)
+ {
+ // check Hyper-V root partition
+ info = CpuId.Invoke(0x40000000);
+ if (info[1] == 0x7263694d && info[2] == 0x666f736f && info[3] == 0x76482074) // "Microsoft Hv"
+ {
+ info = CpuId.Invoke(0x40000003);
+ if ((info[1] & 1) != 0)
+ return false;
+ }
+ return true;
+ }
+ else
+ {
+ int size;
+ uint tableSignature = (byte)'R' << 24 | (byte)'S' << 16 | (byte)'M' << 8 | (byte)'B';
+ try
+ {
+ size = (int)Win32.EnumSystemFirmwareTables(tableSignature, IntPtr.Zero, 0);
+ }
+ catch (EntryPointNotFoundException) { size = 0; }
+
+ if (size > 0)
+ {
+ IntPtr nativeBuffer = Marshal.AllocHGlobal(size);
+ Win32.EnumSystemFirmwareTables(tableSignature, nativeBuffer, (uint)size);
+ byte[] buffer = new byte[size];
+ Marshal.Copy(nativeBuffer, buffer, 0, size);
+ Marshal.FreeHGlobal(nativeBuffer);
+ for (var i = 0; i < size / sizeof(uint); i += sizeof(uint))
+ {
+ uint tableId = BitConverter.ToUInt32(buffer, i);
+ int dataSize = (int)Win32.GetSystemFirmwareTable(tableSignature, tableId, IntPtr.Zero, 0);
+ if (dataSize > 0)
+ {
+ nativeBuffer = Marshal.AllocHGlobal(dataSize);
+ Win32.GetSystemFirmwareTable(tableSignature, tableId, nativeBuffer, (uint)dataSize);
+ byte[] data = new byte[dataSize];
+ Marshal.Copy(nativeBuffer, data, 0, dataSize);
+ Marshal.FreeHGlobal(nativeBuffer);
+ if (FindFirmwareVendor(data))
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public static bool IsValidImageCRC()
+ {
+ if (GlobalData.IsPatchDetected())
+ return false;
+
+ long instance = Marshal.GetHINSTANCE(typeof(Core).Module).ToInt64();
+
+ // check memory CRC
+ var crcSize = (uint)Marshal.ReadInt32(new IntPtr(instance + (uint)Faces.CRC_TABLE_SIZE));
+ var crcPosition = (uint)Faces.CRC_TABLE_ENTRY;
+ bool isValid = true;
+ var crcInfo = new byte[12];
+ var crc32 = new CRC32();
+ var crcHash = (uint)Marshal.ReadInt32(new IntPtr(instance + (uint)Faces.CRC_TABLE_HASH));
+ if (crcHash != crc32.Hash(new IntPtr(instance + crcPosition), crcSize))
+ isValid = false;
+ {
+ var crcCryptor = new CRCValueCryptor();
+ for (var i = 0; i < crcSize; i += crcInfo.Length)
+ {
+ Marshal.Copy(new IntPtr(instance + crcPosition + i), crcInfo, 0, crcInfo.Length);
+ uint address = crcCryptor.Decrypt(BitConverter.ToUInt32(crcInfo, 0));
+ uint size = crcCryptor.Decrypt(BitConverter.ToUInt32(crcInfo, 4));
+ uint hash = crcCryptor.Decrypt(BitConverter.ToUInt32(crcInfo, 8));
+
+ if (crc32.Hash(new IntPtr(instance + address), size) != hash)
+ isValid = false;
+ }
+ }
+
+ // check header and loader CRC
+ crcSize = (uint)Marshal.ReadInt32(new IntPtr(instance + GlobalData.LoaderCrcSize()));
+ crcPosition = GlobalData.LoaderCrcInfo();
+ crcHash = (uint)Marshal.ReadInt32(new IntPtr(instance + GlobalData.LoaderCrcHash()));
+ if (crcHash != crc32.Hash(new IntPtr(instance + crcPosition), crcSize))
+ isValid = false;
+ {
+ var crcCryptor = new CRCValueCryptor();
+ for (var i = 0; i < crcSize; i += crcInfo.Length)
+ {
+ Marshal.Copy(new IntPtr(instance + crcPosition + i), crcInfo, 0, crcInfo.Length);
+ uint address = crcCryptor.Decrypt(BitConverter.ToUInt32(crcInfo, 0));
+ uint size = crcCryptor.Decrypt(BitConverter.ToUInt32(crcInfo, 4));
+ uint hash = crcCryptor.Decrypt(BitConverter.ToUInt32(crcInfo, 8));
+
+ if (crc32.Hash(new IntPtr(instance + address), size) != hash)
+ isValid = false;
+ }
+ }
+
+ return isValid;
+ }
+ public static string DecryptString() { return DecryptString((uint)Faces.STORAGE_INFO); }
+ public static bool FreeString(ref string value) { value = null; return true; }
+ public static SerialState SetSerialNumber(string serial)
+ {
+ LicensingManager licensing_manager = Instance.LicensingManager;
+ return licensing_manager != null ? licensing_manager.SetSerialNumber(serial) : SerialState.Corrupted;
+ }
+ public static SerialState GetSerialNumberState()
+ {
+ LicensingManager licensing_manager = Instance.LicensingManager;
+ return licensing_manager != null ? licensing_manager.GetSerialNumberState() : SerialState.Corrupted;
+ }
+ public static bool GetSerialNumberData(out SerialNumberData data)
+ {
+ data = new SerialNumberData();
+ LicensingManager licensing_manager = Instance.LicensingManager;
+ return licensing_manager != null ? licensing_manager.GetSerialNumberData(data) : false;
+ }
+ public static string GetCurrentHWID() { return Instance.HWID.ToString(); }
+ public static ActivationStatus ActivateLicense(string code, out string serial)
+ {
+ serial = string.Empty;
+ LicensingManager licensing_manager = Instance.LicensingManager;
+ return licensing_manager != null ? licensing_manager.ActivateLicense(code, out serial) : ActivationStatus.NotAvailable;
+ }
+ public static ActivationStatus DeactivateLicense(string serial)
+ {
+ LicensingManager licensing_manager = Instance.LicensingManager;
+ return licensing_manager != null ? licensing_manager.DeactivateLicense(serial) : ActivationStatus.NotAvailable;
+ }
+ public static ActivationStatus GetOfflineActivationString(string code, out string buf)
+ {
+ buf = string.Empty;
+ LicensingManager licensing_manager = Instance.LicensingManager;
+ return licensing_manager != null ? licensing_manager.GetOfflineActivationString(code, out buf) : ActivationStatus.NotAvailable;
+ }
+ public static ActivationStatus GetOfflineDeactivationString(string serial, out string buf)
+ {
+ buf = string.Empty;
+ LicensingManager licensing_manager = Instance.LicensingManager;
+ return licensing_manager != null ? licensing_manager.GetOfflineDeactivationString(serial, out buf) : ActivationStatus.NotAvailable;
+ }
+
+ public static readonly Core Instance = new Core();
+ public HardwareID HWID { get; private set; }
+ public StringManager StringManager { get; private set; }
+ public LicensingManager LicensingManager { get; private set; }
+ public ResourceManager ResourceManager { get; private set; }
+
+ private static string DecryptString(uint stringId) { return Instance.StringManager.DecryptString(stringId); }
+ public static void ShowMessage(string message) { Win32.ShowMessage(message, Assembly.GetExecutingAssembly().GetName().Name, MessageBoxButtons.OK, MessageBoxIcon.Error); }
+
+ private static void AntidebugThread()
+ {
+ Win32.NtSetInformationThread(Win32.CurrentThread, Win32.THREADINFOCLASS.ThreadHideFromDebugger, IntPtr.Zero, 0);
+
+ while (true)
+ {
+ if (Debugger.IsAttached || Debugger.IsLogging())
+ Environment.FailFast("");
+
+ if (Win32.IsDebuggerPresent() || Win32.CheckRemoteDebuggerPresent())
+ Environment.FailFast("");
+
+ Thread.Sleep(1000);
+ }
+ }
+
+ public bool Init(long instance)
+ {
+ var options = (uint)Faces.CORE_OPTIONS;
+ if ((options & (uint)CoreOption.CHECK_DEBUGGER) != 0)
+ {
+ var thread = new Thread(AntidebugThread);
+ thread.IsBackground = true;
+ thread.Start();
+ }
+
+ HWID = new HardwareID();
+
+ var pos = (uint)Faces.STRING_INFO;
+ if (pos != 0)
+ StringManager = new StringManager(instance);
+
+ pos = (uint)Faces.LICENSE_INFO;
+ if (pos != 0)
+ LicensingManager = new LicensingManager(instance);
+
+ pos = (uint)Faces.TRIAL_HWID;
+ if (pos != 0) {
+ var key = new byte[8];
+ Marshal.Copy(new IntPtr(instance + (uint)Faces.KEY_INFO), key, 0, key.Length);
+
+ var entry = new byte[64];
+ Marshal.Copy(new IntPtr(instance + pos), entry, 0, entry.Length);
+ CipherRC5 cipher = new CipherRC5(key);
+ entry = cipher.Decrypt(entry);
+
+ var value = new byte[(uint)Faces.TRIAL_HWID_SIZE];
+ Array.Copy(entry, 0, value, 0, value.Length);
+
+ if (!HWID.IsCorrect(value))
+ {
+ ShowMessage("This application cannot be executed on this computer.");
+ return false;
+ }
+ }
+
+ pos = (uint)Faces.RESOURCE_INFO;
+ if (pos != 0)
+ ResourceManager = new ResourceManager(instance);
+
+ return true;
+ }
+
+ public static uint DecryptBuffer(uint p3, uint p2, uint p1, uint p0)
+ {
+ return Instance.LicensingManager.DecryptBuffer(p3, p2, p1, p0);
+ }
+
+#if !RUNTIME
+ public readonly Random RndGenerator = new Random();
+
+ public uint Rand32()
+ {
+ var a4 = new byte[4];
+ RndGenerator.NextBytes(a4);
+ return BitConverter.ToUInt32(a4, 0);
+ }
+
+ public ulong Rand64()
+ {
+ return (Rand32() << 32) | Rand32();
+ }
+#endif
+ }
+
+ public class ResourceManager
+ {
+ public ResourceManager(long instance)
+ {
+ _instance = instance;
+ var key = new byte[8];
+ Marshal.Copy(new IntPtr(instance + (uint)Faces.KEY_INFO), key, 0, key.Length);
+ _cipher = new CipherRC5(key);
+
+ var resourceInfo = new byte[16];
+ var pos = (uint)Faces.RESOURCE_INFO;
+ var buffer = new byte[1024];
+ for (var i = 0; ;i += resourceInfo.Length)
+ {
+ Marshal.Copy(new IntPtr(instance + pos + i), resourceInfo, 0, resourceInfo.Length);
+ byte[] entry = _cipher.Decrypt(resourceInfo);
+ uint nameOffset = BitConverter.ToUInt32(entry, 0);
+ if (nameOffset == 0)
+ break;
+
+ for (var j = 0; j < buffer.Length / 2; j++)
+ {
+ var c = (ushort)(Marshal.ReadInt16(new IntPtr(instance + nameOffset + j * 2)) ^ (BitRotate.Left((uint)Faces.STRING_DECRYPT_KEY, j) + j));
+ if (c == 0)
+ {
+ _entries.Add(Encoding.Unicode.GetString(buffer, 0, j * 2), (uint)(pos + i));
+ break;
+ }
+ buffer[j * 2] = (byte)c;
+ buffer[j * 2 + 1] = (byte)(c >> 8);
+ }
+ }
+
+ AppDomain.CurrentDomain.AssemblyResolve += Handler;
+ }
+
+ private void DecryptData(ref byte[] data, uint key)
+ {
+ for (var c = 0; c < data.Length; c++)
+ {
+ data[c] = (byte)(data[c] ^ BitRotate.Left(key, c) + c);
+ }
+ }
+
+ private Assembly Handler(object sender, ResolveEventArgs args)
+ {
+ uint pos;
+ if (_entries.TryGetValue(args.Name, out pos))
+ {
+ var resourceInfo = new byte[16];
+ Marshal.Copy(new IntPtr(_instance + pos), resourceInfo, 0, resourceInfo.Length);
+ byte[] entry = _cipher.Decrypt(resourceInfo);
+ uint dataOffset = BitConverter.ToUInt32(entry, 4);
+ var data = new byte[BitConverter.ToUInt32(entry, 8)];
+ Marshal.Copy(new IntPtr(_instance + dataOffset), data, 0, data.Length);
+ DecryptData(ref data, (uint)Faces.STRING_DECRYPT_KEY);
+ try
+ {
+ var assembly = Assembly.Load(data);
+ return assembly;
+ }
+ catch (Exception)
+ {
+
+ }
+ }
+
+ return null;
+ }
+
+ private long _instance;
+ private CipherRC5 _cipher;
+ private readonly Dictionary<string, uint> _entries = new Dictionary<string, uint>();
+ }
+} \ No newline at end of file