新版本出来后,本来想找个可用的“和谐”版本,仍然并没有找到keygen或者Key,只能自己动手了。
if (true) { } return Local.GetString("The licensee or key is not valid."); IL_80:
但是分析代码的时候,有特征的switch语句,应该是有控制流混淆,先去混淆:
λ "D:\Program Files\de4dot\de4dot.exe" -r . -ru -ro "E:\XXX\AxureRP\Axure-rp9\out" de4dot v3.1.41592.3405 Copyright (C) 2011-2014 [email protected] Latest version and source code: https://github.com/0xd4d/de4dot Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\AxDoc.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\AxureRP9.exe) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\AxureRPUpdater.exe) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\Client.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\Client.Mac.exe) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\Client.Win32.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\Component.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\Core.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\FPRPC.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\Generators.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\ImportVectorShapes.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\Model.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\Platform.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\Platform.Mac.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\Platform.Win32.dll) Detected Dotfuscator 19310:1:0:4.9.2500.21407 (D:\Program Files (x86)\Axure\Axure RP 9\Win32Wrapper.dll)
从结果看混淆都成功了,这里可见软件处理的并不强,工具直接处理了!
将输出的文件替换到安装目录后,再dyspy调试:
从代码中可以看到语句都正常了,混淆后的变量不可恢复,这个不太影响分析。
对错误结果设置断点后,通过查看“调用堆栈”直接定位到关键处理函数:
private bool method_39(out string string_2, bool bool_1, bool bool_2) { this.method_26(); // 清空变量 string_2 = this.method_35(); // 获取输入Key值 int i = this.method_35().Length % 4; // 进行 if (i != 0) { while (i < 4) { string_2 += "="; // 长度不为4的倍数,补==,这个字符串是base64编码??? i++; } } LicenseErrorType licenseErrorType; if (!GClass403.smethod_2(this.method_34(), // 验证, method_34()获取用户名 string_2, // Key out licenseErrorType, bool_1)) { string @string = Errors.GetString(licenseErrorType, Configurations.ProductName(true, false)); if (licenseErrorType != LicenseErrorType.IncorrectLicensee) { if (licenseErrorType - LicenseErrorType.InvalidKeyForProduct > 1) { this.axLabel_5.TextColorProp = "WarningColor"; this.axLabel_5.Text = @string; } else { this.axLabel_5.TextColorProp = "PrimaryTextColor"; this.axLabel_5.SetTextAndParseTaggedLinks(@string, null, null); } } else { this.axLabel_6.Text = @string; } return false; } if (this.method_34().Any() && bool_2) { this.axLabel_5.TextColorProp = "SuccessColor"; this.axLabel_5.Text = Local.GetString("This is a valid license."); } return true; } private static GClass738 smethod_63(string string_6, string string_7) { if (string_7.Length == 0) { return null; } if (GClass739.smethod_61(string_7)) // 内置了一个黑名单,进行判断Key是不是在 { GClass738 gclass = new GClass738(); gclass.method_1(LicenseExpirationState.Blocked); // debug enter here . return gclass; } byte[] byte_; try { byte_ = Convert.FromBase64String(string_7); // base64解码成byte } catch (FormatException) { GClass738 gclass2 = new GClass738(); gclass2.method_1(LicenseExpirationState.None); return gclass2; } // GClass739.list_0 版本信息:Team, Pro, Enterprise, using (List<AxPair<RpEdition, GClass736>>.Enumerator enumerator = GClass739.list_0.GetEnumerator()) { while (enumerator.MoveNext()) { AxPair<RpEdition, GClass736> axPair = enumerator.Current; RpEdition first = axPair.First; // 证书类型信息 byte[] array = axPair.Second.vmethod_3(byte_); // 解密信息,AES算法???。 if (array != null) { DateTime dateTime_; try { //将解码后数据的array[16, 23]的8字节转成Int64的日期信息。 DateTime dateTime = new DateTime(BitConverter.ToInt64(array, 16)); if (!(dateTime < new DateTime(2002, 1, 1)) && !(dateTime > new DateTime(2050, 12, 31))) { dateTime_ = dateTime; goto IL_C9; } } catch (ArgumentOutOfRangeException exception_) { Logging.LogException(exception_); } continue; IL_C9: byte[] array2 = new byte[2]; Array.Copy(array, 24, array2, 0, 2); Term term = (Term)array[26]; if (!term.Subscription()) { term = Term.Perpetual; } GClass738 gclass3 = new GClass738(); gclass3.method_11(first); // gclass3.method_1(GClass739.smethod_64(string_6, array, term, dateTime_)); // 过期校验。 gclass3.method_7(dateTime_); gclass3.method_5(string_7); gclass3.method_3(string_6); gclass3.method_9(array2); gclass3.method_13(term); return gclass3; } } goto IL_14A; } GClass738 result; return result; IL_14A: GClass738 gclass4 = new GClass738(); gclass4.method_1(LicenseExpirationState.None); return gclass4; }
从上面的分析可以发现,解密居然用的是AES(对称加密),这样就比较低端了,Keygen是妥妥的。
// Key黑名单。 private static readonly string[] string_5 = new string[] { "dcENpZgjsYDoFPNYx9JMm77TfrzIMlv5iXfdwturL4pDe7DEHjEfjvfCodDKdjLr", "FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq+7w1RH97k5MWctqVHA", "ogj1xp3rOKIyiMCh0w9ZIlujXKeBOE0CeuEjX2yNfDnO1IsJd3xdd7jMSe/iO2Ly", "pxZdYswBY1nPcIbP0iaLzNW2FGmLZBMIAgJKPeIxJL39FlKh0QwMhZ1Q/H15U8fT", ... } private static bool smethod_65(string string_6, byte[] byte_0) { GClass737 gclass = new GClass737(GClass737.GEnum51.const_4); //MD5 gclass.method_3("sa;jl02 34oja a 9es8r23j4k@#%%^9345jfsd"); byte[] bytes = Encoding.UTF8.GetBytes(string_6.Trim().ToLowerInvariant()); byte[] array = new byte[bytes.Length + byte_0.Length - 16]; Array.Copy(bytes, 0, array, 0, bytes.Length); // 把byte_0[16, ...] copy 到 array[16, ...] Array.Copy(byte_0, // sourceArray 16, // sourceIndex array, // destinationArray bytes.Length, // destinationIndex byte_0.Length - 16); // length byte[] array2 = gclass.method_0(array); // Md5,也就是说把用户名作为MD5的密钥对数据进行了加密。 for (int i = 0; i < 16; i++) // 前16位比较。 { if (array2[i] != byte_0[i]) { return false; // 输入的Licensee不对。 } } return true; }
从上面的过程,基本上把一个解密后的Key的组成做了分析,以一个过期的Key为例:
'licensee' : 'Koshy',
'key' : "wTADPqxn3KChzJxLmUr5jTTitCgsfRkftQQ1yIG9HmK83MYSm7GPxLREGn+Ii6xY",
解密结果: \x36\xBC\x2A\x31\x06\x29\xBC\xC9 // md5 encrypted hash \xF4\xC5\xFA\x7B\x26\xAD\x06\xE3 // \xFB\xD7\xE9\xF0\xA1\x6D\xD5\x08 // DateTime \x00\x00 // [24:25] Term bytes: \x00 // Term:[0:Perpetual, 1:Monthly, 2:Annual] \x77\x6A\xD8\x59\xB2 // ? \xFB\xD5\x04\x58\x4E\x47\x45\x4E // ? 1. 解密共40位,前16位为后面Bytes + licensee 的Md5结果。 2. [16, 23]位为过期时间的ticsk编码(C#有点坑,它的时间居然不是1970开妈的,是从01.01.01的ticks) 3. [24,25]位为二进制Term 4. [26]为数值Term信息。 5. 剩下的没找到代码分析,不清楚是不是随机值,作为padding部分吧。
Demo Key:
Licensee : test
Key : b'1PIJJuSV4jEqGezB9qMKhuiniCU+NeezfRehr7cjTBAzSVUr9HGMyrUxPeuvmN6o'
网络验证? 目前没有找到,找到再看看。然后就没有然后了,感觉功能挺强大,但是授权做的太弱了!!!
[2020元旦礼物]《看雪论坛精华17》发布!(补齐之前所有遗漏版本)!
最后于 2天前 被nevinhappy编辑 ,原因: 上传附件。