[.NET]某 尖顶.办公 系列组件 许可证分析
前情提要
看了下论坛,虽然有几篇和这个组件相关的帖子,但似乎没有看到完整的许可分析贴,
所以就弄一个吧,不知道这个层次够不够。
嘉宾介绍
官网地址:aHR0cHM6Ly93d3cuZS1pY2VibHVlLmNvbS9JbnRyb2R1Y2Uvc3BpcmUtb2ZmaWNlLWZvci1uZXQuaHRtbA==
许可证文档:aHR0cHM6Ly93d3cuZS1pY2VibHVlLmNvbS9UdXRvcmlhbHMvTGljZW5zaW5nL0xpY2Vuc2luZy5odG1s
准备工作
包括之前的帖子 首先就是 Nuget 导包,这个真没啥好说的...
然后 弄个控制台程序
然后 Main 里面贴代码
private static void SpireOfficeTest()
{
//Spire.License.LicenseProvider.SetLicenseKey("");
var document = new Spire.Doc.Document();
var section = document.AddSection();
var paragraph = section.AddParagraph();
paragraph.AppendText("Hello World");
document.SaveToFile("Hello Wrold.doc", Spire.Doc.FileFormat.Doc);
Console.WriteLine("Word文档创建成功!");
//Create a new workbook
var workbook = new Spire.Xls.Workbook();
//Initialize worksheet
var sheet = workbook.Worksheets[0];
//Append text
sheet.Range["A1"].Text = "Demo: Save Excel in .NET";
//Save it as Excel file
workbook.SaveToFile("Sample.xls", ExcelVersion.Version97to2003);
Console.WriteLine("Excel 创建成功!");
BarcodeSettings bs = new BarcodeSettings();
bs.Type = BarCodeType.Code39;
bs.Data = "*ABC 12345* ";
BarCodeGenerator bg = new BarCodeGenerator(bs);
bg.GenerateImage().Save("Code39Code.png");
Console.WriteLine("BarCode 创建成功!");
PdfDocument pdf = new Spire.Pdf.PdfDocument();
PdfPageBase page = pdf.Pages.Add();
PdfTrueTypeFont font = new PdfTrueTypeFont(@"C:\WINDOWS\Fonts\CONSOLA.TTF", 20f, PdfFontStyle.Underline);
PdfSolidBrush brush = new PdfSolidBrush(new PdfRGBColor(System.Drawing.Color.Blue));
page.Canvas.DrawString("Hello E-iceblue Support Team", font, brush, new PointF(10, 20));
pdf.SaveToFile("Result.pdf", Spire.Pdf.FileFormat.PDF);
Console.WriteLine("PDF 创建成功!");
//create PPT document
Presentation presentation = new Spire.Presentation.Presentation();
//add new shape to PPT document
IAutoShape shape = presentation.Slides[0]
.Shapes.AppendShape(Spire.Presentation.ShapeType.Rectangle,
new RectangleF(0, 50, 200, 50));
shape.ShapeStyle.LineColor.Color = Color.White;
shape.Fill.FillType = Spire.Presentation.Drawing.FillFormatType.None;
//add text to shape
shape.AppendTextFrame("Hello World!");
//set the Font fill style of text
var textRange = shape.TextFrame.TextRange;
textRange.Fill.FillType = Spire.Presentation.Drawing.FillFormatType.Solid;
textRange.Fill.SolidColor.Color = Color.Black;
textRange.LatinFont = new TextFont("Arial Black");
//save the document
presentation.SaveToFile("hello.pptx", Spire.Presentation.FileFormat.Pptx2010);
Console.WriteLine("PPT 创建成功!");
var addressFrom = new Spire.Email.MailAddress("daisy.zhang@e-iceblue.com", "Daisy Zhang");
var addressTo = new Spire.Email.MailAddress("susanwong32@outlook.com");
var mail = new Spire.Email.MailMessage(addressFrom, addressTo);
mail.Subject = "This is a test message";
string htmlString = @"
<p>Dear Ms. Susan,</p>
<p>This is an example of creating an <b>outlook message</b> <u>(msg)</u>.</p>
<ul>
<li> Create a message with RTF file </li>
<li> Create a message with attachment</li>
</ul>
<p style='color:red'>This text is in red </p>
<p>Best regards, </p>
<p>Daisy </p>";
mail.BodyHtml = htmlString;
mail.Attachments.Add(new Spire.Email.Attachment("Code39Code.png"));
mail.Save("Sample.msg", Spire.Email.MailMessageFormat.Msg);
Console.WriteLine("Mail 创建成功!");
}
开工
我们直接从Demo中查一下 Spire.License.LicenseProvider.SetLicenseKey
这玩意的所属。
发现 所属于 Spire.Pdf.dll
。
后面专门分析它就行了。
简单看一眼 有字符串混淆
为了方便 分析和理解,老样子 de4dot 处理下先。
由于该库用了类似韩文混淆,后续为了方便解说,我会使用 dnspy 适当修改一些字段或函数名便于理解
自然是 先分析 SetLicenseKey
这个函数了,并分析调用。
// Spire.License.LicenseProvider
// Token: 0x06011D49 RID: 73033
private static spr癝_LicenseInfo 권_LoadLicenseByLicenseKey(string A_0)
{
return spr禛.께_LoadLicenseByLicenseKey(A_0);
}
继续追
然后看 DecodeAndCheckKey
// spr禛
// Token: 0x06010117 RID: 65815
private static byte[] 권_DecodeAndCheck(string A_0_licenseKey, bool A_1_hashBytes)
{
byte[] array = Convert.FromBase64String(A_0_licenseKey); //licenseKey 是 base64编码的
byte[] array2 = new byte[15];
//复制前15个字节
Array.Copy(array, array2, array2.Length);
//取第一个字节的值,然后对13取余
int num = (int)(array2[0] % 13);
//取第num+1个字节的值,然后与255取与,再左移8位,然后与第num+2个字节的值与255取与,最后取或
//也就是取第num+1个字节和第num+2个字节的值,然后组成一个short类型的值
byte[] array3 = new byte[((int)(array2[num + 1] & byte.MaxValue) << 8) | (int)(array2[num + 2] & byte.MaxValue)];
//然后将 15 之后 到 array3 长度的字节 复制到 array3
//所以 ((int)(array2[num + 1] & byte.MaxValue) << 8) | (int)(array2[num + 2] & byte.MaxValue) 为 array 的长度
//然后 结合下文 可以知道 array3 是 sign,array4 是 验签data
Array.Copy(array, 15, array3, 0, array3.Length);
num = array2.Length + array3.Length;
byte[] array4 = new byte[array.Length - num]; // head[15] + sign 之后的数据 全取出来 为 data
Array.Copy(array, num, array4, 0, array4.Length);
using (RSACryptoServiceProvider rsacryptoServiceProvider = new RSACryptoServiceProvider())
{
rsacryptoServiceProvider.ImportParameters(new RSAParameters
{
Modulus = spr瑫.권_RSA_Modulus,
Exponent = spr瑫.귟_RSA_Exponent
});
if (!rsacryptoServiceProvider.VerifyData(array4, new SHA1CryptoServiceProvider(), array3)) //验证签名
{
return null;
}
}
byte[] array5 = new byte[(int)array4[0]];
Array.Copy(array4, 1, array5, 0, array5.Length);
byte[] array6 = new byte[array4.Length - 1 - array5.Length];
Array.Copy(array4, 1 + array5.Length, array6, 0, array6.Length);
byte[] array7 = null;
//根据 array4 首位来截取 DES 的 IV
//然后 将 之后的数据 进行解密
using (DESCryptoServiceProvider descryptoServiceProvider = new DESCryptoServiceProvider())
{
descryptoServiceProvider.Key = spr瑫.긲_DES_Key;
descryptoServiceProvider.IV = array5;
using (MemoryStream memoryStream = new MemoryStream(array6))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, descryptoServiceProvider.CreateDecryptor(), CryptoStreamMode.Read))
{
using (MemoryStream memoryStream2 = new MemoryStream())
{
byte[] array8 = new byte[1024];
int num2;
while ((num2 = cryptoStream.Read(array8, 0, array8.Length)) > 0)
{
memoryStream2.Write(array8, 0, num2);
}
array7 = memoryStream2.ToArray();
}
}
}
}
//DES 解密后的数据 用一个自定义算法计算一下 hash
if (A_1_hashBytes)
{
spr禛.긲_LicenseBytesHash = spr禛.권_HashBytes(array7);
}
return array7;
}
由上述解码函数可以得知
//LicenseKey 由 15字节的head + sign + data 后 base64 组成
//data 由 DESIV.Len + DESIV + DES(LicenseBytes) 组成
//其中 heade 只有 根据首位 计算的下标 +1 和 +2 的值有用 对应实现运算后 要等与 sing 的长度
//伪代码
data = DESIV.Len + DESIV + DES(LicenseBytes)
sign = RSASign(data)
head = 随机生成15字节()
int num = head[0] % 13
calcSignLen(sign.Len,out int n1,out int n2);
head[num+1] = n1;
head[num+2] = n2;
LicenseKey = base64(head + sign + data)
接着 继续看 LicenseBytes 是怎么解析的
// spr禛
// Token: 0x0601010D RID: 65805
private static spr矼_LicenseInfo 귟_ParseLicenseStream(Stream A_0)
{
if (A_0 == null)
{
return null;
}
spr矼_LicenseInfo spr矼_LicenseInfo = null;
A_0.Position = 0L;
using (XmlReader xmlReader = XmlReader.Create(A_0))
{
bool flag = false;
bool flag2 = false;
while (!xmlReader.EOF)
{
//License 节点
if (xmlReader.LocalName == "License")
{
if (!xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
if (spr矼_LicenseInfo == null)
{
spr矼_LicenseInfo = new spr矼_LicenseInfo();
}
string attribute = xmlReader.GetAttribute("Key"); //License Key 子节点
string attribute2 = xmlReader.GetAttribute("Version");//License Version 子节点
spr矼_LicenseInfo.꼫_SetKey(attribute);
spr矼_LicenseInfo.嘬_SetVersion(attribute2);
flag = true;
}
}
//ServerInfo 节点
else if (xmlReader.LocalName == "ServerInfo" && !xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
if (spr矼_LicenseInfo == null)
{
spr矼_LicenseInfo = new spr矼_LicenseInfo();
}
spr矼_LicenseInfo.꽾_SetServerInfoString(xmlReader.ReadInnerXml());
flag2 = true;
}
if (flag && flag2)
{
break;
}
xmlReader.Read();
spr禛.권_XmlReaderNext(xmlReader);
}
}
return spr矼_LicenseInfo;
}
比较直观和简洁 LicenseBytes 是个 XML 里面 有 License 和 ServerInfo 两个节点。
// spr禛_LicenseManager
// Token: 0x0601010E RID: 65806
private static spr癝_LicenseInfo 권_ParseLicenseStream2(Stream A_0)
{
if (A_0 == null)
{
return null;
}
spr癝_LicenseInfo spr癝_LicenseInfo = new spr癝_LicenseInfo();
A_0.Position = 0L;
using (XmlReader xmlReader = XmlReader.Create(A_0))
{
while (!xmlReader.EOF)
{
string localName = xmlReader.LocalName;
uint num = spr縥.권(localName); //这个不用管 类似控制流 混淆 决定下一步判断走哪儿 不影响分析
if (num <= 1127555431U)
{
if (num <= 470340825U)
{
if (num != 426894090U)
{
if (num == 470340825U)
{
if (localName == "Username" && !xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(xmlReader.Value))
{
xmlReader.Read();
spr禛_LicenseManager.권_XmlReaderNext(xmlReader);
}
if (xmlReader.NodeType == XmlNodeType.Text)
{
spr癝_LicenseInfo.귟_SetUsername(xmlReader.Value);
}
}
}
}
else if (localName == "Organization" && !xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(xmlReader.Value))
{
xmlReader.Read();
spr禛_LicenseManager.권_XmlReaderNext(xmlReader);
}
if (xmlReader.NodeType == XmlNodeType.Text)
{
spr癝_LicenseInfo.꺅_SetOrganization(xmlReader.Value);
}
}
}
else if (num != 795632884U)
{
if (num != 954666724U)
{
if (num == 1127555431U)
{
if (localName == "Email" && !xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(xmlReader.Value))
{
xmlReader.Read();
spr禛_LicenseManager.권_XmlReaderNext(xmlReader);
}
if (xmlReader.NodeType == XmlNodeType.Text)
{
spr癝_LicenseInfo.긲_SetEmail(xmlReader.Value);
}
}
}
}
else if (localName == "License" && !xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
string attribute = xmlReader.GetAttribute("Key");
string attribute2 = xmlReader.GetAttribute("Version");
spr癝_LicenseInfo.꼫_SetKey(attribute);
spr癝_LicenseInfo.嘬_SetVersion(attribute2);
}
}
else if (localName == "LicensedDate" && !xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(xmlReader.Value))
{
xmlReader.Read();
spr禛_LicenseManager.권_XmlReaderNext(xmlReader);
}
DateTime dateTime;
if (xmlReader.NodeType == XmlNodeType.Text && DateTime.TryParse(xmlReader.Value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dateTime))
{
spr癝_LicenseInfo.권_SetLicensedDate(dateTime);
}
}
}
else if (num <= 3213063382U)
{
if (num != 2565758308U)
{
if (num == 3213063382U)
{
if (localName == "Issuer" && !xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
spr禛_LicenseManager.귟_SetIssuer(xmlReader, spr癝_LicenseInfo);
}
}
}
else if (localName == "SerialNumber" && !xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(xmlReader.Value))
{
xmlReader.Read();
spr禛_LicenseManager.권_XmlReaderNext(xmlReader);
}
if (xmlReader.NodeType == XmlNodeType.Text)
{
spr癝_LicenseInfo.권_SerialNumber(xmlReader.Value);
}
}
}
else if (num != 3316904022U)
{
if (num != 3408678061U)
{
if (num == 3512062061U)
{
if (localName == "Type" && !xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(xmlReader.Value))
{
xmlReader.Read();
spr禛_LicenseManager.권_XmlReaderNext(xmlReader);
}
if (xmlReader.NodeType == XmlNodeType.Text)
{
spr癝_LicenseInfo.권_LicenseType(spr禛_LicenseManager.꺅_ParseLicenseType(xmlReader.Value));
}
}
}
}
else if (localName == "Products" && !xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
spr禛_LicenseManager.권_SetProducts(xmlReader, spr癝_LicenseInfo);
}
}
else if (localName == "ExpiredDate" && !xmlReader.IsEmptyElement && xmlReader.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(xmlReader.Value))
{
xmlReader.Read();
spr禛_LicenseManager.권_XmlReaderNext(xmlReader);
}
DateTime dateTime2;
if (xmlReader.NodeType == XmlNodeType.Text && DateTime.TryParse(xmlReader.Value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dateTime2))
{
spr癝_LicenseInfo.귟_ExpiredDate(dateTime2);
}
}
xmlReader.Read();
spr禛_LicenseManager.권_XmlReaderNext(xmlReader);
}
}
return spr癝_LicenseInfo;
}
// spr禛_LicenseManager
// Token: 0x0601010F RID: 65807
private static void 귟_SetIssuer(XmlReader A_0, spr癝_LicenseInfo A_1)
{
if (A_0 != null && A_1 != null)
{
if (A_0.LocalName == "Issuer")
{
if (!A_0.IsEmptyElement)
{
A_1.권_SetIssuerInfo(new spr皰_IssuerInfo());
}
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
while (A_0.LocalName != "Issuer")
{
string localName = A_0.LocalName;
if (!(localName == "Name"))
{
if (!(localName == "Email"))
{
if (localName == "Url" && !A_0.IsEmptyElement && A_0.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(A_0.Value))
{
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
}
if (A_0.NodeType == XmlNodeType.Text)
{
A_1.뀤_GetIssuerInfo().긲_SetUrl(A_0.Value);
}
}
}
else if (!A_0.IsEmptyElement && A_0.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(A_0.Value))
{
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
}
if (A_0.NodeType == XmlNodeType.Text)
{
A_1.뀤_GetIssuerInfo().귟_SetEmail(A_0.Value);
}
}
}
else if (!A_0.IsEmptyElement && A_0.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(A_0.Value))
{
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
}
if (A_0.NodeType == XmlNodeType.Text)
{
A_1.뀤_GetIssuerInfo().권_SetName(A_0.Value);
}
}
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
}
}
return;
}
}
// spr禛_LicenseManager
// Token: 0x06010110 RID: 65808
private static void 권_SetProducts(XmlReader A_0, spr癝_LicenseInfo A_1)
{
if (A_0 != null && A_1 != null)
{
if (A_0.LocalName == "Products")
{
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
List<spr眃_ProductInfo> list = new List<spr眃_ProductInfo>();
spr眃_ProductInfo spr眃_ProductInfo = new spr眃_ProductInfo();
while (A_0.LocalName != "Products")
{
string localName = A_0.LocalName;
if (!(localName == "Product"))
{
if (!(localName == "Name"))
{
if (!(localName == "Version"))
{
if (localName == "Subscription" && !A_0.IsEmptyElement && A_0.NodeType == XmlNodeType.Element)
{
spr禛_LicenseManager.권_SetSubscription(A_0, spr眃_ProductInfo);
}
}
else if (!A_0.IsEmptyElement && A_0.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(A_0.Value))
{
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
}
if (A_0.NodeType == XmlNodeType.Text)
{
spr眃_ProductInfo.귟_SetVersion(A_0.Value);
}
}
}
else if (!A_0.IsEmptyElement && A_0.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(A_0.Value))
{
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
}
if (A_0.NodeType == XmlNodeType.Text)
{
spr眃_ProductInfo.권_Name(A_0.Value);
}
}
}
else if (!A_0.IsEmptyElement && A_0.NodeType == XmlNodeType.Element)
{
spr眃_ProductInfo = new spr眃_ProductInfo();
list.Add(spr眃_ProductInfo);
}
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
}
if (list.Count > 0)
{
A_1.권_SetProductInfos(list.ToArray());
}
}
return;
}
}
// spr禛_LicenseManager
// Token: 0x06010111 RID: 65809
private static void 권_SetSubscription(XmlReader A_0, spr眃_ProductInfo A_1)
{
if (A_0 != null && A_1 != null)
{
if (A_0.LocalName == "Subscription")
{
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
A_1.권_SubscriptionInfo(new spr睖_SubscriptionInfo());
while (A_0.LocalName != "Subscription")
{
string localName = A_0.LocalName;
if (!(localName == "NumberOfPermittedDeveloper"))
{
if (!(localName == "NumberOfPermittedSite"))
{
if (localName == "NumberOfServerLicenses" && !A_0.IsEmptyElement && A_0.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(A_0.Value))
{
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
}
if (A_0.NodeType == XmlNodeType.Text)
{
string value = A_0.Value;
int num;
if (!string.IsNullOrEmpty(value) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
{
A_1.긲_SubscriptionInfo().긲_NumberOfServerLicenses(num);
}
}
}
}
else if (!A_0.IsEmptyElement && A_0.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(A_0.Value))
{
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
}
if (A_0.NodeType == XmlNodeType.Text)
{
string value2 = A_0.Value;
int num2;
if (!string.IsNullOrEmpty(value2) && int.TryParse(value2, NumberStyles.Integer, CultureInfo.InvariantCulture, out num2))
{
A_1.긲_SubscriptionInfo().귟_NumberOfPermittedSite(num2);
}
}
}
}
else if (!A_0.IsEmptyElement && A_0.NodeType == XmlNodeType.Element)
{
if (string.IsNullOrEmpty(A_0.Value))
{
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
}
if (A_0.NodeType == XmlNodeType.Text)
{
string value3 = A_0.Value;
int num3;
if (!string.IsNullOrEmpty(value3) && int.TryParse(value3, NumberStyles.Integer, CultureInfo.InvariantCulture, out num3))
{
A_1.긲_SubscriptionInfo().권_NumberOfPermittedDeveloper(num3);
}
}
}
A_0.Read();
spr禛_LicenseManager.권_XmlReaderNext(A_0);
}
}
return;
}
}
上面的也没啥好说的 就是 单纯的 解析 XML 根据上面的 代码 可以得到 许可证的 XML 模板
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<License Key="" Version="">
<Type></Type>
<Username></Username>
<Email></Email>
<Organization></Organization>
<LicensedDate></LicensedDate>
<ExpiredDate></ExpiredDate>
<Products>
<Product>
<Name></Name>
<Version></Version>
<Subscription>
<NumberOfServerLicenses></NumberOfServerLicenses>
<NumberOfPermittedDeveloper></NumberOfPermittedDeveloper>
<NumberOfPermittedSite></NumberOfPermittedSite>
</Subscription>
</Product>
</Products>
<Issuer>
<Name></Name>
<Email></Email>
<Url></Url>
</Issuer>
</License>
<ServerInfo>
</ServerInfo>
但是 我们好像还没碰到这些值具体都有什么用。
那就 接着往上层回溯
发现 红框中的 赋值 好像并没有 在之前的解析中 涉及到,咱们查一下 谁改动和读取了这里。
// spr璾
// Token: 0x0601007F RID: 65663
internal static spr癝_LicenseInfo 권_VerifyLicense(spr癝_LicenseInfo A_0, Type A_1, object A_2)
{
if (A_0 == null)
{
return null;
}
Assembly assembly = Assembly.GetAssembly(A_1);
AssemblyName name = assembly.GetName();
string text = spr璾.권(assembly);
text = text.Replace(".Office.", ".");
PackageAttribute[] array = PackageAttribute.GetPackage(assembly); // 取程序集上的 Package 特性
PackageAttribute packageAttribute = null;
DateTime? dateTime;
PackageAttribute[] array2 = spr璾.권(assembly, array, out dateTime);
// 这里又过了一次 特性校验 有兴趣可以自己深入
// dateTime 默认是 程序集特性 [assembly: ReleaseDate("2023-04-27")] 但是 会过一次解码验证 可能被重新赋值。
// 由于我们对这些不做改动 所以略过
if (array2 != null)
{
array = array2;
}
spr眃_ProductInfo spr眃_ProductInfo = null;
spr眃_ProductInfo[] array3 = A_0.꿑_GetProductInfos();
int i = 0;
while (i < array3.Length)
{
spr眃_ProductInfo spr眃_ProductInfo2 = array3[i];
string text2 = spr眃_ProductInfo2.권_Name().Replace(".Office.", ".");
//版本号得有效 至少大于 1.3
if (((A_0.녰_GetMergedLicenseVersionInfo() != null && A_0.녰_GetMergedLicenseVersionInfo().권_DiffVersion(1, 3) >= 0) || (A_0.녰_GetMergedLicenseVersionInfo() == null && A_0.뉩_GetLicenseVersionInfo().권_DiffVersion(1, 3) >= 0)) && array != null && array.Length != 0)
{
PackageAttribute[] array4 = array;
int j = 0;
while (j < array4.Length)
{
PackageAttribute packageAttribute2 = array4[j];
if (!packageAttribute2.Name.Equals(text2))
{
j++;
}
else
{
packageAttribute = packageAttribute2;
spr眃_ProductInfo = spr眃_ProductInfo2;
IL_EC:
if (spr眃_ProductInfo == null)
{
goto IL_F0;
}
goto IL_111; // license 中的 Product 的 Name 至少要和 Package 中的 一个一致。
}
}
goto IL_EC;
}
IL_F0:
if (!text.Equals(text2))
{
i++;
continue;
}
spr眃_ProductInfo = spr眃_ProductInfo2;
IL_111:
if (spr眃_ProductInfo == null)
{
return null;
}
if (spr璾.권_CheckBlackList(A_0)) //判断 黑名单,自己构建的许可证可以无视这个
{
A_0.긲_InValid(true); //在就知道 之前没涉及到 字段 true 的情况为失效
A_0.께_ErrorMsg("Your license has been blacklisted, please contact E-ICEBLUE sales to obtain a new license.");
return A_0;
}
spr瞩_SubscriptionType spr瞩_SubscriptionType = spr眃_ProductInfo.긲_SubscriptionInfo().께_SubscriptionType();
if (spr瞩_SubscriptionType == spr瞩_SubscriptionType.꼫_CloudServer) //云
{
// CloudServer 的订阅模式需要额外解析 ServerInfo 我们不需要 可以略过
if (string.IsNullOrEmpty(A_0.늼_GetServerInfoString()))
{
return null;
}
List<spr畤> list = spr甑.꺅();
if (list == null && list.Count <= 0)
{
return null;
}
string text3 = spr甑.권(A_0.늼_GetServerInfoString());
spr疷 spr疷 = new spr疷(text3);
if (string.IsNullOrEmpty(spr疷.권()) || string.IsNullOrEmpty(spr疷.귟()))
{
return null;
}
bool flag = false;
foreach (spr畤 spr畤 in list)
{
if (!flag && spr疷.귟().Contains(spr畤.귟()) && spr疷.권().Contains(spr畤.긲()))
{
flag = true;
break;
}
}
if (!flag)
{
return null;
}
}
//LicenseType 为 demo 或 unknow 需要 验证 过期时间是否小于当前时间
if (A_0.귟_LicenseType() == spr碢_LicenseType.귟_demo || A_0.귟_LicenseType() == spr碢_LicenseType.꺅_unknow)
{
DateTime dateTime2 = DateTime.Now.ToUniversalTime();
A_0.긲_InValid(A_0.꽾_ExpiredDate() < dateTime2);
}
if (A_0.덢_InValid()) //失效就直接返回
{
return A_0;
}
if (dateTime != null) //如果程序集有发布时间 则 ExpiredDate 需要 大于 发布时间,ExpiredDate 看来是必填
{
A_0.긲_InValid(A_0.꽾_ExpiredDate() < dateTime);
}
if (A_0.덢_InValid()) //失效就直接返回
{
return A_0;
}
// LicensedDate 和 ExpiredDate 再和 ProductVersion 做了一层比较,不重要 大就完事了!
int num = spr璾.권(A_0.꼫_GetLicensedDate(), A_0.꽾_ExpiredDate());
if (spr璾.권(spr璾.권(spr眃_ProductInfo.귟_GetVersion(), A_0.꼫_GetLicensedDate()), (packageAttribute != null) ? packageAttribute.Version : string.Format("{0}.{1}", name.Version.Major, name.Version.Minor)) > num)
{
A_0.긲_InValid(true);
}
if (A_0.덢_InValid())
{
return A_0;
}
//如果之前没判定为失效的话
//如果订阅类型为 Developer 或 SiteEnterprise 且 ExpiredDate 大于当前时间
//会将 License 上报到 服务器???
if (!A_0.덢_InValid() && (spr瞩_SubscriptionType == spr瞩_SubscriptionType.귟_Developer || spr瞩_SubscriptionType == spr瞩_SubscriptionType.꺅_SiteEnterprise) && A_0.꽾_ExpiredDate() >= DateTime.Now)
{
try
{
if (!spr笺.꺅_IsInServerBlackList()) //如果服务器返回 1 或者 别的 就会标记为 true。 然后 再走就默认失效了。
{
spr笺.긲_ReportLicenseToServer(A_0.넝_GetSerialNumberOrMD5LicenseKey(), A_0.긲_GetUsername(), (int)spr瞩_SubscriptionType, spr竧.권_DateFormate(A_0.꽾_ExpiredDate()));
goto IL_36A;
}
A_0.긲_InValid(true);
}
catch (Exception ex)
{
spr竧.권("LicenseUtilities_Validate", ex);
goto IL_36A;
}
return A_0;
}
IL_36A:
if (A_0.귟_LicenseType() == spr碢_LicenseType.꺅_unknow)
{
A_0.권_LicenseType(spr碢_LicenseType.긲_runtime);
}
return A_0;
}
goto IL_111;
}
所以 我们 不能 配置为 Developer 或 SiteEnterprise 的订阅。
internal spr瞩_SubscriptionType 께_SubscriptionType()
{
if (this.긲_NumberOfServerLicenses() > 0)
{
return spr瞩_SubscriptionType.꼫_CloudServer;
}
if (this.권_NumberOfPermittedDeveloper() == 1 && this.귟_NumberOfPermittedSite() == 1)
{
return spr瞩_SubscriptionType.귟_Developer;
}
if (this.권_NumberOfPermittedDeveloper() == 1 && this.귟_NumberOfPermittedSite() == 2147483647)
{
return spr瞩_SubscriptionType.긲_DeveloperOEM;
}
if (this.권_NumberOfPermittedDeveloper() == 10 && this.귟_NumberOfPermittedSite() == 10)
{
return spr瞩_SubscriptionType.꺅_SiteEnterprise;
}
if (this.권_NumberOfPermittedDeveloper() == 50 && this.귟_NumberOfPermittedSite() == 2147483647)
{
return spr瞩_SubscriptionType.께_SiteOEM;
}
return spr瞩_SubscriptionType.귟_Developer;
}
可选的就是 DeveloperOEM 或 SiteOEM。
我们再来看 最后一个函数
那么 License 完整答案就有了!其他没什么用的 拿掉拿掉~
const string licTemplate = @"<?xml version=""1.0"" encoding=""utf-8"" standalone=""yes""?>
<License Key=""52Pojie"" Version=""999.999"">
<Type>Runtime</Type>
<Username>52Pojie</Username>
<LicensedDate>2099-01-01T12:00:00Z</LicensedDate>
<ExpiredDate>2099-12-31T12:00:00Z</ExpiredDate>
<Products>
<Product>
<Name>Spire.Office Platinum</Name>
<Version>999.999</Version>
<Subscription>
<NumberOfPermittedDeveloper>50</NumberOfPermittedDeveloper>
<NumberOfPermittedSite>2147483647</NumberOfPermittedSite>
</Subscription>
</Product>
</Products>
</License>";
生成许可证