[C#] 纯文本查看 复制代码
using System;
using System.IO;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
namespace CmRestore;
// ---------------------------------------------------------------------------
// 1. 机器码
// raw20 = CPUID.0:EBX || CPUID.0:EDX || CPUID.0:ECX || CPUID.1:EAX || 卷序列号
// 机器码 = BASE64( raw20[i] ^ (wrap[i] ^ wrap[16+(i&15)] ^ (i*19+7)) )
// ---------------------------------------------------------------------------
public static class MachineCode
{
public static byte[] BuildRaw20(uint cpuid0Ebx, uint cpuid0Edx, uint cpuid0Ecx,
uint cpuid1Eax, uint volumeSerial)
{
var b = new byte[20];
void Put(int o, uint v)
{
b[o] = (byte)v; b[o + 1] = (byte)(v >> 8);
b[o + 2] = (byte)(v >> 16); b[o + 3] = (byte)(v >> 24);
}
Put(0, cpuid0Ebx); Put(4, cpuid0Edx); Put(8, cpuid0Ecx);
Put(12, cpuid1Eax); Put(16, volumeSerial);
return b;
}
/// <summary>raw20 -> 机器码(BASE64,28 字符)</summary>
public static string Encode(byte[] raw20, byte[] wrap)
=> Convert.ToBase64String(Xor(raw20, wrap));
/// <summary>机器码(BASE64) -> raw20</summary>
public static byte[] Decode(string machineCodeB64, byte[] wrap)
=> Xor(Convert.FromBase64String(machineCodeB64.Trim()), wrap);
private static byte[] Xor(byte[] raw20, byte[] wrap)
{
var o = new byte[20];
for (int i = 0; i < 20; i++)
o[i] = (byte)(raw20[i] ^ wrap[i] ^ wrap[16 + (i & 15)] ^ (i * 19 + 7));
return o;
}
// --- 本机信息采集(Windows)---
public static uint VolumeSerial()
{
var winDir = new StringBuilder(260);
if (GetWindowsDirectoryW(winDir, 260) == 0 || winDir.Length < 1) return 0;
GetVolumeInformationW(winDir[0] + ":\\", null, 0, out uint serial,
IntPtr.Zero, IntPtr.Zero, null, 0);
return serial;
}
/// <summary>取 CPUID.EAX/EBX/ECX/EDX(用一段自造 stub,与原程序思路一致)</summary>
public static void Cpuid(uint leaf, out uint a, out uint b, out uint c, out uint d)
{
uint[] o = new uint[4];
var code = IntPtr.Size == 8
? new byte[] { 0x53, 0x56, 0x48, 0x89, 0xD6, 0x89, 0xC8, 0x31, 0xC9,
0x0F, 0xA2, 0x89, 0x06, 0x89, 0x5E, 0x04, 0x89, 0x4E,
0x08, 0x89, 0x56, 0x0C, 0x5E, 0x5B, 0xC3 }
: new byte[] { 0x53, 0x56, 0x8B, 0x44, 0x24, 0x0C, 0x8B, 0x74, 0x24, 0x10,
0x31, 0xC9, 0x0F, 0xA2, 0x89, 0x06, 0x89, 0x5E, 0x04, 0x89,
0x4E, 0x08, 0x89, 0x56, 0x0C, 0x5E, 0x5B, 0xC2, 0x08, 0x00 };
IntPtr mem = VirtualAlloc(IntPtr.Zero, (UIntPtr)code.Length, 0x3000, 0x40);
Marshal.Copy(code, 0, mem, code.Length);
var h = GCHandle.Alloc(o, GCHandleType.Pinned);
try
{
var f = (CpuidStub)Marshal.GetDelegateForFunctionPointer(mem, typeof(CpuidStub));
f(leaf, h.AddrOfPinnedObject());
}
finally { h.Free(); VirtualFree(mem, UIntPtr.Zero, 0x8000); }
a = o[0]; b = o[1]; c = o[2]; d = o[3];
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate void CpuidStub(uint leaf, IntPtr dst);
public static byte[] LocalRaw20()
{
Cpuid(0, out _, out uint b0, out uint c0, out uint d0);
Cpuid(1, out uint a1, out _, out _, out _);
return BuildRaw20(b0, d0, c0, a1, VolumeSerial());
}
[DllImport("kernel32", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern uint GetWindowsDirectoryW(StringBuilder b, uint n);
[DllImport("kernel32", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern bool GetVolumeInformationW(string r, StringBuilder n, uint nc,
out uint serial, IntPtr cc, IntPtr fs, StringBuilder fn, uint fnc);
[DllImport("kernel32", ExactSpelling = true)]
private static extern IntPtr VirtualAlloc(IntPtr a, UIntPtr s, uint t, uint p);
[DllImport("kernel32", ExactSpelling = true)]
private static extern bool VirtualFree(IntPtr a, UIntPtr s, uint t);
}
// ---------------------------------------------------------------------------
// 2. Ed25519(等价实现,用 BigInteger 写清楚;验签结果与原程序 TweetNaCl 版一致)
// ---------------------------------------------------------------------------
public static class Ed25519
{
private static readonly BigInteger P = BigInteger.Pow(2, 255) - 19;
private static readonly BigInteger L = BigInteger.Pow(2, 252)
+ BigInteger.Parse("27742317777372353535851937790883648493");
private static readonly BigInteger D = (-121665 * ModInv(121666)) % P;
private static readonly BigInteger Bx = BigInteger.Parse(
"15112221349535400772501151409588531511454012693041857206046113283949847762202");
private static BigInteger ModInv(BigInteger a) => BigInteger.ModPow(a, P - 2, P);
/// <summary>小端、无符号解析</summary>
private static BigInteger U(byte[] b) => new BigInteger(b, isUnsigned: true, isBigEndian: false);
private static BigInteger Mod(BigInteger a) { a %= P; return a < 0 ? a + P : a; }
private static BigInteger Sqrt(BigInteger v)
{
BigInteger x = BigInteger.ModPow(v, (P + 3) / 8, P);
if (x * x % P != Mod(v)) x = x * BigInteger.ModPow(2, (P - 1) / 4, P) % P;
return x;
}
/// <summary>由 4/5 反推基点 y</summary>
private static readonly BigInteger By = Mod(4 * ModInv(5));
private static (BigInteger x, BigInteger y)? Decompress(byte[] p)
{
BigInteger y = U(p) & (BigInteger.Pow(2, 255) - 1); // 去掉符号位
if (y >= P) return null;
BigInteger xx = Mod((y * y - 1) * ModInv(Mod(D * y * y + 1)));
BigInteger x = Sqrt(xx);
if (x * x % P != xx) return null;
if ((x & BigInteger.One) != (p[31] >> 7)) x = P - x;
return (x, y);
}
private static (BigInteger x, BigInteger y) Add((BigInteger x, BigInteger y) p,
(BigInteger x, BigInteger y) q)
{
BigInteger t = Mod(D * p.x * q.x * p.y * q.y);
BigInteger x = Mod((p.x * q.y + q.x * p.y) * ModInv(Mod(1 + t)));
BigInteger y = Mod((p.y * q.y + p.x * q.x) * ModInv(Mod(1 - t)));
return (x, y);
}
private static (BigInteger x, BigInteger y) Mul((BigInteger x, BigInteger y) p, BigInteger e)
{
var r = (BigInteger.Zero, BigInteger.One);
e %= L; if (e < 0) e += L;
for (int i = 0; i < 256; i++)
{
if (((e >> i) & 1) != 0) r = Add(r, p);
p = Add(p, p);
}
return r;
}
private static readonly (BigInteger x, BigInteger y) B = (Bx, By);
private static BigInteger HashMod(byte[] data)
{
byte[] h = SHA512.HashData(data);
var v = U(h) % L; // 小端、无符号
return v < 0 ? v + L : v;
}
public static bool Verify(byte[] pk, byte[] msg, byte[] sig)
{
if (pk.Length != 32 || sig.Length != 64) return false;
var A = Decompress(pk); var R = Decompress(sig[..32]);
if (A == null || R == null) return false;
BigInteger raw = U(sig[32..64]);
if (raw >= L) return false;
BigInteger S = raw;
byte[] buf = new byte[64 + msg.Length];
Buffer.BlockCopy(sig, 0, buf, 0, 32);
Buffer.BlockCopy(pk, 0, buf, 32, 32);
Buffer.BlockCopy(msg, 0, buf, 64, msg.Length);
BigInteger h = HashMod(buf);
var lhs = Mul(B, S); // S*B
var rhs = Add(R.Value, Mul(A.Value, h)); // R + h*A
return lhs.x == rhs.x && lhs.y == rhs.y;
}
public static byte[] PublicKey(byte[] seed32)
{
byte[] h = SHA512.HashData(seed32);
var a = new byte[32]; Buffer.BlockCopy(h, 0, a, 0, 32);
a[0] &= 248; a[31] &= 127; a[31] |= 64;
BigInteger s = U(a) % L;
var A = Mul(B, s);
return Compress(A);
}
public static byte[] Sign(byte[] seed32, byte[] msg)
{
byte[] h = SHA512.HashData(seed32);
var a = new byte[32]; Buffer.BlockCopy(h, 0, a, 0, 32);
a[0] &= 248; a[31] &= 127; a[31] |= 64;
BigInteger s = U(a) % L;
byte[] prefix = new byte[32]; Buffer.BlockCopy(h, 32, prefix, 0, 32);
byte[] rm = new byte[32 + msg.Length];
Buffer.BlockCopy(prefix, 0, rm, 0, 32);
Buffer.BlockCopy(msg, 0, rm, 32, msg.Length);
BigInteger r = HashMod(rm);
byte[] R = Compress(Mul(B, r));
byte[] pk = PublicKey(seed32);
byte[] hm = new byte[64 + msg.Length];
Buffer.BlockCopy(R, 0, hm, 0, 32);
Buffer.BlockCopy(pk, 0, hm, 32, 32);
Buffer.BlockCopy(msg, 0, hm, 64, msg.Length);
BigInteger k = HashMod(hm);
BigInteger S = (r + k * s) % L;
byte[] sig = new byte[64];
Buffer.BlockCopy(R, 0, sig, 0, 32);
byte[] sb = S.ToByteArray(isUnsigned: true, isBigEndian: false);
Buffer.BlockCopy(sb, 0, sig, 32, Math.Min(32, sb.Length));
return sig;
}
private static byte[] Compress((BigInteger x, BigInteger y) p)
{
byte[] o = new byte[32];
byte[] y = p.y.ToByteArray(isUnsigned: true, isBigEndian: false);
Buffer.BlockCopy(y, 0, o, 0, Math.Min(32, y.Length));
o[31] |= (byte)(((int)(p.x & BigInteger.One)) << 7);
return o;
}
}
// ---------------------------------------------------------------------------
// 3. 授权核心
// ---------------------------------------------------------------------------
public sealed class CmLicense
{
public byte[] Pack { get; }
public byte[] Magic => Pack[0..4];
public byte[] Tag => Pack[20..28];
public byte[] Wrap => Pack[28..60];
public int Rounds => Pack[7] == 0 ? 16 : Pack[7] * 8;
public uint Poly => BitConverter.ToUInt32(Pack, 8);
public byte[] PublicKey => KeyMaterial().pk;
public byte[] Twist => KeyMaterial().twist;
public CmLicense(byte[] pack)
{
if (pack == null || pack.Length < 2048) throw new ArgumentException("pack");
Pack = pack;
}
/// <summary>从 CM.exe 中提取 pack(按魔数 80 54 5A 5D 定位,长度取 8000)</summary>
public static CmLicense FromExe(string path, int packLength = 8000)
{
byte[] all = File.ReadAllBytes(path);
for (int i = 0; i + 4 < all.Length; i++)
{
if (all[i] == 0x80 && all[i + 1] == 0x54 && all[i + 2] == 0x5A && all[i + 3] == 0x5D)
return new CmLicense(all[i..(i + packLength)]);
}
throw new FileNotFoundException("pack 魔数未找到");
}
/// <summary>解出内嵌公钥(32) 与 twist(16)</summary>
private (byte[] pk, byte[] twist) KeyMaterial()
{
byte[] d = (byte[])Pack[1996..2044].Clone();
for (int i = 0; i < d.Length; i++)
{
int n = 8 - (i % 7 + 1);
byte x = (byte)((d[i] << n) | (d[i] >> (8 - n)));
x ^= Magic[i & 3];
x ^= (byte)(Poly >> (8 * (i & 3)));
x -= Wrap[i & 31];
x ^= Tag[i & 7];
x = (byte)(x - (i * 19 + Rounds));
d[i] = x;
}
return (d[0..32], d[32..48]);
}
private static uint Rotl(uint x, int n) { n &= 31; return (x << n) | (x >> (32 - n)); }
/// <summary>96 字节授权块的流式加/解密(异或流,自反)</summary>
private void StreamCrypt(byte[] buf, byte[] raw20)
{
byte[] hex = Encoding.ASCII.GetBytes(Convert.ToHexString(raw20));
uint[] s = new uint[8];
s[0] = 0xA541A6D5 ^ BitConverter.ToUInt32(raw20, 0);
s[1] = 0xC4D3B2A1 ^ BitConverter.ToUInt32(raw20, 4);
s[2] = 0x9E3779B9 ^ BitConverter.ToUInt32(raw20, 8);
s[3] = 0x7F4A7C15 ^ BitConverter.ToUInt32(raw20, 12);
s[4] = BitConverter.ToUInt32(Wrap, 0) ^ BitConverter.ToUInt32(Magic, 0);
s[5] = BitConverter.ToUInt32(Wrap, 8) ^ BitConverter.ToUInt32(Tag, 0);
s[6] = BitConverter.ToUInt32(Wrap, 16) ^ BitConverter.ToUInt32(hex, 0);
s[7] = BitConverter.ToUInt32(Wrap, 24) ^ BitConverter.ToUInt32(hex, 36);
for (int i = 0; i < Rounds; i++)
{
uint t = unchecked(Rotl(s[i & 7], 7) + s[(i + 3) & 7] + (uint)(i * 2654435769u));
s[(i + 1) & 7] ^= t;
s[(i + 5) & 7] += Rotl(t, 13);
}
for (int j = 0; j < buf.Length; j++)
{
uint v = unchecked(Rotl(s[j & 7], j % 19 + 1) + Wrap[j & 31] + Tag[j & 7]
+ Magic[j & 3] + hex[j % hex.Length]);
s[j & 7] = v;
buf[j] ^= (byte)(v >> ((j & 3) * 8));
}
}
/// <summary>签名消息 = F(header || raw20 || magic),自定义 32 字节海绵</summary>
private static byte[] MakeMessage(byte[] header, byte[] raw20, byte[] magic, byte[] twist)
{
byte[] a = new byte[32];
byte[] src = new byte[56];
Buffer.BlockCopy(header, 0, src, 0, 32);
Buffer.BlockCopy(raw20, 0, src, 32, 20);
Buffer.BlockCopy(magic, 0, src, 52, 4);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 56; j++)
{
int n = j & 31;
int t = a[n] + src[j] + twist[j & 15] + i * 17;
t ^= a[(n + 13) & 31];
a[n] = (byte)((t << 3) | (t >> 5)); // 注意 t 可 >255
a[(n + 7) & 31] ^= (byte)(t * 131);
}
return a;
}
private static byte[] UnmaskSignature(byte[] sig, byte[] twist, byte[] wrap)
{
byte[] o = new byte[64];
for (int i = 0; i < 64; i++)
o[i] = (byte)(sig[i] ^ twist[i & 15] ^ wrap[i & 31] ^ (i * 29 + 11));
return o;
}
private static byte[] HexDecode(string s)
{
var o = new System.Collections.Generic.List<byte>();
int acc = 0, cnt = 0;
foreach (char c in s)
{
int v = c is >= '0' and <= '9' ? c - 48
: c is >= 'A' and <= 'F' ? c - 65 + 10
: c is >= 'a' and <= 'f' ? c - 97 + 10 : -1;
if (v < 0) continue;
acc = (acc << 4) | v;
if (++cnt == 2) { o.Add((byte)acc); acc = 0; cnt = 0; }
}
return o.ToArray();
}
public static int Today => (int)(DateTime.UtcNow.Date - new DateTime(2020, 1, 1)).TotalDays;
/// <summary>完整校验(等价于原程序 _醾嬶鹬b0倞769)</summary>
public (bool ok, string why) Check(string codeHex, string machineCodeB64, int? today = null)
{
byte[] blob = HexDecode(codeHex);
if (blob.Length != 96) return (false, "长度不是 96 字节");
byte[] raw20 = MachineCode.Decode(machineCodeB64, Wrap);
StreamCrypt(blob, raw20);
byte[] header = blob[0..32], sig = blob[32..96];
if (!header.AsSpan(0, 4).SequenceEqual(Magic)) return (false, "magic 不匹配");
var (pk, twist) = KeyMaterial();
sig = UnmaskSignature(sig, twist, Wrap);
byte[] m = MakeMessage(header, raw20, Magic, twist);
if (!Ed25519.Verify(pk, m, sig)) return (false, "签名无效");
if (BitConverter.ToUInt32(header, 24) != BitConverter.ToUInt32(raw20, 12)) return (false, "CPU 签名不符");
if (BitConverter.ToUInt32(header, 28) != BitConverter.ToUInt32(raw20, 16)) return (false, "卷序列号不符");
ushort d0 = BitConverter.ToUInt16(header, 4), d1 = BitConverter.ToUInt16(header, 6);
int now = today ?? Today;
if (now < d0 || now > d1) return (false, $"不在有效期 [{d0},{d1}],今天 {now}");
return (true, $"有效 有效期天数[{d0},{d1}] share={Convert.ToHexString(header[8..24])}");
}
public bool CheckWithPublicKey(string codeHex, string machineCodeB64, byte[] pk, int? today = null)
{
byte[] blob = HexDecode(codeHex);
if (blob.Length != 96) return false;
byte[] raw20 = MachineCode.Decode(machineCodeB64, Wrap);
StreamCrypt(blob, raw20);
byte[] header = blob[0..32], sig = blob[32..96];
var (_, twist) = KeyMaterial();
sig = UnmaskSignature(sig, twist, Wrap);
byte[] m = MakeMessage(header, raw20, Magic, twist);
int now = today ?? Today;
return Ed25519.Verify(pk, m, sig)
&& BitConverter.ToUInt32(header, 24) == BitConverter.ToUInt32(raw20, 12)
&& BitConverter.ToUInt32(header, 28) == BitConverter.ToUInt32(raw20, 16)
&& now >= BitConverter.ToUInt16(header, 4) && now <= BitConverter.ToUInt16(header, 6);
}
/// <summary>生成 lic(需要 Ed25519 私钥种子 —— 程序里只有公钥)</summary>
public string Build(byte[] seed32, string machineCodeB64, ushort d0, ushort d1, byte[] share16 = null)
{
byte[] raw20 = MachineCode.Decode(machineCodeB64, Wrap);
var (_, twist) = KeyMaterial();
share16 ??= RandomNumberGenerator.GetBytes(16);
var header = new byte[32];
Buffer.BlockCopy(Magic, 0, header, 0, 4);
BitConverter.GetBytes(d0).CopyTo(header, 4);
BitConverter.GetBytes(d1).CopyTo(header, 6);
Buffer.BlockCopy(share16, 0, header, 8, 16);
Buffer.BlockCopy(raw20, 12, header, 24, 4);
Buffer.BlockCopy(raw20, 16, header, 28, 4);
byte[] m = MakeMessage(header, raw20, Magic, twist);
byte[] sig = UnmaskSignature(Ed25519.Sign(seed32, m), twist, Wrap);
var blob = new byte[96];
Buffer.BlockCopy(header, 0, blob, 0, 32);
Buffer.BlockCopy(sig, 0, blob, 32, 64);
StreamCrypt(blob, raw20);
return Convert.ToHexString(blob);
}
}
// ---------------------------------------------------------------------------
// 自测入口
// ---------------------------------------------------------------------------
public static class Program
{
public static void Main(string[] args)
{
string exe = args.Length > 0 ? args[0] : @"C:\Users\Administrator\Downloads\CM\CM\CM.exe";
string lic = args.Length > 1 ? args[1] : @"C:\Users\Administrator\Downloads\CM\CM\lic.key";
string mcFile = args.Length > 2 ? args[2] : @"C:\Users\Administrator\Downloads\CM\CM\对应的机器码.txt";
var cm = CmLicense.FromExe(exe);
Console.WriteLine($"magic = {Convert.ToHexString(cm.Magic)}");
Console.WriteLine($"wrap = {Convert.ToHexString(cm.Wrap)}");
Console.WriteLine($"tag = {Convert.ToHexString(cm.Tag)}");
Console.WriteLine($"poly = 0x{cm.Poly:x8} rounds = {cm.Rounds}");
Console.WriteLine($"内嵌公钥 = {Convert.ToHexString(cm.PublicKey)}");
Console.WriteLine($"twist = {Convert.ToHexString(cm.Twist)}");
Console.WriteLine();
string code = File.ReadAllText(lic).Trim();
string mc = File.ReadAllText(mcFile).Trim();
var (ok, why) = cm.Check(code, mc);
Console.WriteLine($"官方 lic.key 校验 -> {ok} ({why})");
byte[] raw = MachineCode.Decode(mc, cm.Wrap);
Console.WriteLine($"机器码还原 raw20 = {Convert.ToHexString(raw)} ({Encoding.ASCII.GetString(raw[..12])})");
byte[] seed = RandomNumberGenerator.GetBytes(32);
string mine = cm.Build(seed, mc, 1000, 60000);
Console.WriteLine($"自制 lic 往返校验 -> {cm.CheckWithPublicKey(mine, mc, Ed25519.PublicKey(seed))}");
Console.WriteLine($"本机机器码 = {MachineCode.Encode(MachineCode.LocalRaw20(), cm.Wrap)}");
}
}