嵌入式系统神器 FreeRTOS+Trace 破解分析
本帖最后由 ZCShou 于 2017-1-21 13:19 编辑本人搞嵌入式的,基本整天和C及汇编打交道,对于C#这个不是很了解,以下分析,仅供学习,如果工作使用还是买正版!
一、准备
首先,下载安装包,直接安装?当然不是!使用UniExtract检查一下,发现直接使用7-zip解压即可!,解压后文件如下:
文件挺多,但是有用没几个,其中,Jlink开头的俩应该是Segger官方的文件,需要处理的应该就是Tracealyzer开头的三个dll和TzForFreeRTOS.exe,使用PEID 检查一下,发现是它们都是.net程序,接着使用.net工具ScanId查询一下,发现有混淆
二、反混淆直接祭出de4dot,只能一次将四个文件全部处理,单个处理后程序无法运行(大神给个解释?)
三、开搞
我在使用 .NET Reflector,因为在保存时提示错误,又不知道如何解决!所以只是用Reflector来查看代码(搜索啥的比较方便),在实际修改时用了GrayWolf!运行一下,看看,直接Enter License 看看,发现网络验证!即便是 Offline 也是创建个本地文件,然后要求将文件以邮件的形式发给厂商!
那怎么搞呢,直接到源码里面找关键字?貌似是可以的!! 还有种方法,界面中有个评估版,点进去后,让填一些信息,点击激活后,会直接跳转到官方网站,然后,你的使用License 就申请成功了!然后在Enter License 在线激活就开启了试用!其会在:系统盘\ProgramData\Tracealyzer for FreeRTOS Data 下生成 License.xml的文件,里面信息还是详细的!
具体,探索过程不说了!直接上关键地方!(这里说明一点,Reflector反编译的代码,明显不如 dnSpy或者GrayWolf整洁)
GClass243.method_11(Byte[], String, DateTime) : GEnum22 // 这个函数就是取License 中个部分用来校验的
Depends On
Used By
ActivateProductForm.method_4(XmlDocument) : Void // 这个看名字就知道,激活窗口,肯定要调用校验License的函数
GClass105.method_14(String) : Void
Depends On
Used By
GClass105.method_12() : Void // 这个是 处理 License结果的,后面这些和界面有关
Depends On
Used By
GClass105.method_0(String[], Action) : Void
Depends On
Used By
FreeRTOS_Trace.Program.smethod_0(String[]) : Void
Depends On
Used By
FreeRTOS_Trace.Program.Main(String[]) : Void
Depends On
Used By
关键点就在看GClass243.method_11这个函数(GrayWolf中代码):
internal GEnum22 method_11(byte[] byte_0, string string_4, DateTime dateTime_1)
{
int num = 0;
switch (num)
{
}
XmlElement xmlElement3;
XmlElement xmlElement9;
while (true)
{
XmlElement xmlElement = this.xmlDocument_0.smethod_7("/SignedLicense/License");
XmlElement xmlElement2 = this.xmlDocument_0.smethod_7("/SignedLicense/Signature");
byte[] bytes = Encoding.UTF8.GetBytes(xmlElement.OuterXml);
byte[] byte_ = Convert.FromBase64String(xmlElement2.InnerText);
xmlElement3 = this.xmlDocument_0.smethod_7("/SignedLicense/License/ProductId");
num = 9;
int num2 = num;
while (true)
{
string b;
string b2;
bool flag;
IEnumerator enumerator2;
DateTime dateTime2;
switch (num2)
{
case 0:
{
if (!GClass246.smethod_0(byte_0, bytes, byte_))
{
num = 4;
num2 = num;
continue;
}
XmlElement xmlElement4 = this.xmlDocument_0.smethod_7("/SignedLicense/License/SupportThrough");
DateTime dateTime;
base.method_5(dateTime = DateTime.Parse(xmlElement4.InnerText));
DateTime t = dateTime;
num = 2;
num2 = num;
continue;
}
case 1:
try
{
num = 2;
num2 = num;
while (true)
{
num = -25162;
int arg_238_0 = num;
num = -25162;
switch ((arg_238_0 == num) ? 1 : 0)
{
case 0:
case 2:
break;
case 1:
goto IL_259;
default:
goto IL_259;
}
IL_24D:
num = 8;
num2 = num;
continue;
IL_259:
num = 0;
if (num != 0)
{
goto IL_260;
}
IL_260:
object current;
switch (num2)
{
case 0:
{
XmlElement xmlElement5 = (XmlElement)current;
IEnumerator enumerator = xmlElement5.ChildNodes.GetEnumerator();
num = 3;
num2 = num;
continue;
}
case 1:
if (current is XmlElement)
{
num = 0;
num2 = num;
continue;
}
goto IL_54F;
case 2:
goto IL_54F;
case 3:
try
{
num = 1;
num2 = num;
while (true)
{
switch (num2)
{
case 0:
goto IL_3AB;
case 1:
goto IL_34D;
case 2:
num = 9;
num2 = num;
continue;
case 3:
num = 4;
num2 = num;
continue;
case 4:
{
XmlElement xmlElement6;
if (xmlElement6.InnerText == "ANY")
{
num = 17;
num2 = num;
continue;
}
goto IL_34D;
}
case 5:
goto IL_4A3;
case 6:
{
IEnumerator enumerator;
if (!enumerator.MoveNext())
{
num = 15;
num2 = num;
continue;
}
object current2 = enumerator.Current;
num = 10;
num2 = num;
continue;
}
case 7:
{
XmlElement xmlElement6;
if (xmlElement6.Name == "Id")
{
num = 2;
num2 = num;
continue;
}
break;
}
case 8:
num = 14;
num2 = num;
continue;
case 9:
{
XmlElement xmlElement6;
if (!(xmlElement6.InnerText == b))
{
num = 8;
num2 = num;
continue;
}
goto IL_3AB;
}
case 10:
{
object current2;
if (current2 is XmlElement)
{
num = 11;
num2 = num;
continue;
}
goto IL_34D;
}
case 11:
{
object current2;
XmlElement xmlElement6 = (XmlElement)current2;
num = 7;
num2 = num;
continue;
}
case 12:
{
XmlElement xmlElement6;
if (xmlElement6.Name == "Id")
{
num = 3;
num2 = num;
continue;
}
goto IL_34D;
}
case 13:
goto IL_4A8;
case 14:
{
XmlElement xmlElement6;
if (xmlElement6.InnerText == b2)
{
num = 0;
num2 = num;
continue;
}
break;
}
case 15:
num = 5;
num2 = num;
continue;
case 16:
goto IL_4AD;
case 17:
flag = true;
num = 13;
num2 = num;
continue;
default:
goto IL_34D;
}
num = 12;
num2 = num;
continue;
IL_34D:
num = 6;
num2 = num;
continue;
IL_3AB:
flag = true;
num = 16;
num2 = num;
}
IL_4A3:
IL_4A8:
IL_4AD:
goto IL_55E;
}
finally
{
while (true)
{
IEnumerator enumerator;
IDisposable disposable = enumerator as IDisposable;
num = 2;
num2 = num;
while (true)
{
switch (num2)
{
case 0:
disposable.Dispose();
num = 1;
num2 = num;
continue;
case 1:
goto IL_4F9;
case 2:
if (disposable != null)
{
num = 0;
num2 = num;
continue;
}
goto IL_4F9;
}
break;
}
}
IL_4F9:;
}
break;
IL_55E:
num = 9;
num2 = num;
continue;
case 4:
goto IL_56D;
case 5:
goto IL_56F;
case 6:
break;
case 7:
num = 5;
num2 = num;
continue;
case 8:
num = 4;
num2 = num;
continue;
case 9:
if (!flag)
{
goto IL_54F;
}
goto IL_24D;
default:
goto IL_54F;
}
if (!enumerator2.MoveNext())
{
num = 7;
num2 = num;
continue;
}
current = enumerator2.Current;
num = 1;
num2 = num;
continue;
IL_54F:
num = 6;
num2 = num;
}
IL_56D:
IL_56F:;
}
finally
{
while (true)
{
IDisposable disposable = enumerator2 as IDisposable;
num = 1;
num2 = num;
while (true)
{
switch (num2)
{
case 0:
goto IL_5B8;
case 1:
if (disposable != null)
{
num = 2;
num2 = num;
continue;
}
goto IL_5B8;
case 2:
disposable.Dispose();
num = 0;
num2 = num;
continue;
}
break;
}
}
IL_5B8:;
}
num = 8;
num2 = num;
continue;
case 2:
{
DateTime t;
if (t < dateTime_1)
{
num = 10;
num2 = num;
continue;
}
XmlElement xmlElement7 = this.xmlDocument_0.smethod_7("/SignedLicense/License/ExpiresOn");
num = 1;
if (num == 0)
{
IL_20C:
num = 13;
num2 = num;
continue;
}
goto IL_20C;
}
case 3:
goto IL_1AD;
case 4:
return GEnum22.const_1;
case 5:
return GEnum22.const_3;
case 6:
try
{
while (true)
{
XmlElement xmlElement8 = this.xmlDocument_0.smethod_7("/SignedLicense/License/ReactivateOnExpire");
num = 3;
num2 = num;
while (true)
{
switch (num2)
{
case 0:
base.method_9(true);
num = 1;
num2 = num;
continue;
case 1:
goto IL_151;
case 2:
goto IL_1A2;
case 3:
if (xmlElement8.InnerText == "True")
{
num = 0;
num2 = num;
continue;
}
goto IL_151;
}
break;
IL_151:
num = 2;
num2 = num;
}
}
IL_1A2:
goto IL_61D;
}
catch (Exception)
{
goto IL_61D;
}
goto IL_1AD;
IL_61D:
num = 3;
num2 = num;
continue;
case 7:
return GEnum22.const_5;
case 8:
if (!flag)
{
num = 7;
num2 = num;
continue;
}
goto IL_694;
case 9:
if (string_4 != xmlElement3.InnerText)
{
num = 11;
num2 = num;
continue;
}
num = 0;
num2 = num;
continue;
case 10:
return GEnum22.const_4;
case 11:
return GEnum22.const_2;
case 12:
{
XmlElement xmlElement7;
dateTime2 = DateTime.Parse(xmlElement7.InnerText);
base.method_7(new DateTime?(dateTime2));
num = 6;
num2 = num;
continue;
}
case 13:
{
XmlElement xmlElement7;
if (xmlElement7.InnerText != "Never")
{
num = 12;
num2 = num;
continue;
}
goto IL_77;
}
}
break;
IL_77:
xmlElement9 = this.xmlDocument_0.smethod_7("/SignedLicense/License/EditionId");
XmlElement xmlElement10 = this.xmlDocument_0.smethod_7("/SignedLicense/License/Nodes");
b = GClass247.smethod_1();
b2 = GClass247.smethod_3();
flag = false;
enumerator2 = xmlElement10.ChildNodes.GetEnumerator();
num = 1;
num2 = num;
continue;
IL_1AD:
if (!(dateTime2 < DateTime.Now.Date))
{
goto IL_77;
}
num = 5;
num2 = num;
}
}
return GEnum22.const_1;
IL_694:
base.method_1(xmlElement3.InnerText);
base.method_3(xmlElement9.InnerText);
return GEnum22.const_0;
}
分析上面代码,可知:
(1)首先,校验ProductId,失败时直接返回 GEnum22.const_2,而在GClass105.method_14中就是用这些值来区分具体错误类型的!
(2)校验Signature(使用了RSACryptoServiceProvider非对称加密),失败时直接返回 GEnum22.const_1
(3)检验SupportThrough,失败时直接返回GEnum22.const_4
(4)校验ExpiresOn,看看是否到期
........
从中,可以分析出,只要返回GEnum22.const_0 ,就是校验成功!看看函数最后,
IL_694:
base.method_1(xmlElement3.InnerText);
base.method_3(xmlElement9.InnerText);
return GEnum22.const_0;
最简单办法直接,跳转到IL_694就OK了!,这样,随便改改License都无所谓了! License中EditionId表示版本类型:
AAE8DBC5-A985-4BC8-819D-5DDABEE7F448 表示 Academic Edition(这个版本官网没看到,但是源码中存在)
E2BB2BF3-507C-44F2-B250-F5734C63C1BD 表示 Professional Edition
2A5BC2B2-177E-4421-AB65-9683EEF175AC 表示 Standard Edition
D0C8EF57-8402-4264-9BBE-4FA2FD3A50B9 表示 Evaluation Edition
133C03FE-FB99-4B25-8FBD-BCD06711018B 表示 Free Edition
程序会读取 License中的 EditionId以区分版本,上面的修改直接将校验返回为了真,所以,改成相应的字符串什么就是对应的版本!
至于具体算法啥的,谁愿意搞谁搞吧!!上面该有的都有了,算法应该不难!
再努力搞下也发篇文章讲讲如何破解Tracealyzer for uCOSIII的。{:1_900:} 本帖最后由 kingdjh 于 2018-4-27 09:12 编辑
ZCShou 发表于 2018-4-23 12:51
破解方法时完全一样的啊,4 的 版本我也试过了!就是4 的验证逻辑不太一样
4 我看了下,把RSA校验去掉,然后就可以为所欲为了。
下面是根据离线文件生成注册文件的代码
using System;
using System.Xml;
namespace TracealyzerCoder
{
class Program
{
static void Main(string[] args)
{
Console.Write("input offline request file name: ");
string filename = Console.ReadLine();
try
{
XmlDocument requestXml = new XmlDocument();
requestXml.Load(filename);
RequestInfo info = XmlGetRequestInfo(requestXml);
GenerateActivationFile(info);
Console.WriteLine("OK");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.Read();
}
static RequestInfo XmlGetRequestInfo(XmlDocument xmlDocument)
{
string[] nodeNames = new[] { "Key", "ProductId", "Id", "Name" };
RequestInfo info = new RequestInfo();
foreach (string name in nodeNames)
{
XmlNodeList nodes = xmlDocument.GetElementsByTagName(name);
if (nodes.Count == 0)
{
throw new Exception("bad format of request file.");
}
else
{
string text = nodes.InnerText;
switch (name)
{
case "Key":
info.key = text;
break;
case "ProductId":
info.productId = text;
break;
case "Id":
info.id = text;
break;
case "Name":
info.name = text;
break;
}
}
}
return info;
}
static void GenerateActivationFile(RequestInfo info)
{
Random rd = new Random();
XmlDocument root = new XmlDocument();
XmlElement signedLicense = root.CreateElement("SignedLicense");
root.AppendChild(signedLicense);
XmlElement license = root.CreateElement("License");
signedLicense.AppendChild(license);
byte[] haskkeyBytes = new byte;
rd.NextBytes(haskkeyBytes);
XmlElement hashKey = root.CreateElement("HashedKey");
hashKey.InnerText = Convert.ToBase64String(haskkeyBytes);
license.AppendChild(hashKey);
XmlElement product = root.CreateElement("Product");
product.InnerText = "Tracealyzer - Standard Edition";
license.AppendChild(product);
XmlElement productId = root.CreateElement("ProductId");
productId.InnerText = info.productId;
license.AppendChild(productId);
XmlElement editionId = root.CreateElement("EditionId");
editionId.InnerText = "{050C587E-D34F-4361-B344-F8FCC4473477}";
license.AppendChild(editionId);
XmlElement licenseTo = root.CreateElement("LicensedTo");
licenseTo.InnerText = info.name;
license.AppendChild(licenseTo);
XmlElement expiresOn = root.CreateElement("ExpiresOn");
expiresOn.InnerText = "Never";
license.AppendChild(expiresOn);
XmlElement supportThrough = root.CreateElement("SupportThrough");
supportThrough.InnerText = "2099-12-31";
license.AppendChild(supportThrough);
license.AppendChild(XmlAddComponents(root));
XmlElement nodes = root.CreateElement("Nodes");
license.AppendChild(nodes);
XmlElement node = root.CreateElement("Node");
nodes.AppendChild(node);
XmlElement id = root.CreateElement("Id");
id.InnerText = info.id;
node.AppendChild(id);
XmlElement name = root.CreateElement("Name");
name.InnerText = info.name;
node.AppendChild(name);
byte[] licenseBytes = new byte;
rd.NextBytes(licenseBytes);
XmlElement signature = root.CreateElement("Signature");
signature.InnerText = Convert.ToBase64String(licenseBytes);
signedLicense.AppendChild(signature);
root.Save("result.xml");
}
static XmlElement XmlAddComponents(XmlDocument root)
{
ComponentXml[] components = new ComponentXml[]
{
new ComponentXml("{B1CF5B88-ADA5-4B2A-81AF-257054106205}", "Tracealyzer Application"),
new ComponentXml("{469D108A-B824-4C27-99ED-98B8629BFCE0}", "FreeRTOS Support"),
new ComponentXml("{95F4ED62-6F8E-4EE3-8ED2-1A6F68B63A50}", "Keil RTX5 Support"),
new ComponentXml("{629259CF-458D-4F05-9545-99DA60198B26}", "µC/OS-III Support")
};
XmlElement componentsXml = root.CreateElement("Components");
foreach (ComponentXml component in components)
{
XmlElement componentXml = root.CreateElement("Component");
XmlElement idXml = root.CreateElement("Id");
idXml.InnerText = component.id;
XmlElement nameXml = root.CreateElement("Name");
nameXml.InnerText = component.name;
componentXml.AppendChild(idXml);
componentXml.AppendChild(nameXml);
componentsXml.AppendChild(componentXml);
}
return componentsXml;
}
}
class RequestInfo
{
public string key;
public string productId;
public string id;
public string name;
}
class ComponentXml
{
public string id;
public string name;
public ComponentXml(string _id, string _name)
{
this.id = _id;
this.name = _name;
}
}
}
去掉RSA校验方法:
用dnSpy打开Tracealyzer.Application.dll,然后搜索"SignedLicense"字符串,一般只有一个方法。
该方法有三个参数,其中一个是个byte[],找到方法内用到这个byte[]的地方(一般只有一个),这个调用就是校验RSA的,NOP掉就行。
sblpp 发表于 2018-4-27 10:18
您好,谢谢您,用您的方法在Tracealyzer.Application.dll中并没有找到SignedLicense字符串,甚至连 icens ...
楼主全才,感谢分享。 支持楼主 支持楼主 强大~ 支持lz 感谢楼主 感谢分析。。。 看到了jlink 好熟悉的感觉啊 一会就不能用了,好难受 agasdf 发表于 2017-1-22 08:44
一会就不能用了,好难受
哪不能用了?