栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

aspose for .net注册继续研究

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

aspose for .net注册继续研究

申明:本帖只用于学习(其实是我自己记录下免得以后忘了),不发成品,大家也别问我要.
用到的工具de4dot,dnspy,vs2019

前几天老板变态的需求,非要把上万个工程的Excel文件转成html让其他人访问,目的是不想让人下载,随便吧,老板最大.
但同一进程打开100个以上文件就报错,于是就出现了上一篇文章.(其实写个进程重新开就行了,不过谁让.net的程序破解简单呢…)
然后逛论坛的时候发现了这个https://www.52pojie.cn/forum.php?mod=viewthread&tid=1116770
然后就觉得可以搞搞,锻炼锻炼智商.
根据该楼主的思路,下了个2012年版本的aspose.cell.开始研究.
先de4net去花,然后dnspy跑起来.(楼主说跑不起来要报错…报错你跳过就是了嘛,反正就只研究注册部分)

以下是部分程序片段

	internal void method_1(Stream stream_0)
		{
			if (stream_0 == null)
			{
				throw new ArgumentNullException("stream");
			}
			Class827.smethod_3(0);
			XmlDocument xmlDocument = new XmlDocument();
			xmlDocument.Load(stream_0);
			this.method_2(xmlDocument);
			Hashtable hashtable = Class828.hashtable_0;
			if (hashtable == null)
			{
				//hashtable = new Hashtable();
				hashtable = Class828.smethod_10();
				Class828.hashtable_0 = hashtable;
			}
			if (hashtable.ContainsKey(this.string_1))
			{
				throw new InvalidOperationException("This license is disabled, please contact Aspose to obtain a new license.");
			}
			if (Class827.smethod_2() > 0)
			{
				return;
			}
			bool flag = false;
			string[] array = this.string_0;
			int i = 0;
			while (i < array.Length)
			{
				string text = array[i];
				if (!text.Equals("Aspose.Total") && !text.Equals("Aspose.Total for .NET") && !text.Equals("Aspose.Cells for .NET") && !text.Equals("Aspose.Cells Product Family") && !text.Equals("Aspose.Total Product Family") && !text.Equals("Aspose.Cells"))
				{
					i++;
				}
				else
				{
					flag = true;
				
					if (!flag)
					{
						throw new InvalidOperationException("The license is not valid for this product.");
					}
					DateTime t = DateTime.ParseExact("2012.01.16", "yyyy.MM.dd", CultureInfo.InvariantCulture);
					if (t > this.dateTime_0)
					{
						throw new InvalidOperationException(string.Format("The subscription included in this license allows free upgrades until {0}, but this version of the product was released on {1}. Please renew the subscription or use a previous version of the product.", this.dateTime_0.ToString("dd MMM yyyy", CultureInfo.InvariantCulture), t.ToString("dd MMM yyyy", CultureInfo.InvariantCulture)));
					}
					if (DateTime.Now > this.dateTime_1)
					{
						throw new InvalidOperationException("The license has expired.");
					}
					this.enum127_0 = Enum127.const_1;
					Class828.class828_0 = this;
					return;
				}
			}
			


			if (!flag)
			{
				throw new InvalidOperationException("The license is not valid for this product.");
			}
			DateTime t1 = DateTime.ParseExact("2012.01.16", "yyyy.MM.dd", CultureInfo.InvariantCulture);
			if (t1 > this.dateTime_0)
			{
				throw new InvalidOperationException(string.Format("The subscription included in this license allows free upgrades until {0}, but this version of the product was released on {1}. Please renew the subscription or use a previous version of the product.", this.dateTime_0.ToString("dd MMM yyyy", CultureInfo.InvariantCulture), t1.ToString("dd MMM yyyy", CultureInfo.InvariantCulture)));
			}
			if (DateTime.Now > this.dateTime_1)
			{
				throw new InvalidOperationException("The license has expired.");
			}
			this.enum127_0 = Enum127.const_1;
			Class828.class828_0 = this;
			return;


		}

先读xml,然后一步一步判断下来,哪里挡住改哪里,没难度.
然后我自信满满的跑去看它的注册算法,为了方便调试和注释,我还把它关于注册的程序挪到了vs里跑.

		private static void smethod_2(XmlNode xmlNode_0, XmlNode xmlNode_1)
		{
			string s = (xmlNode_0 != null) ? xmlNode_0.OuterXml : ""; //data内容
			byte[] bytes = Encoding.Unicode.GetBytes(s); //data字节
			string s2 = (xmlNode_1 != null) ? xmlNode_1.InnerText : "";
			byte[] byte_ = Convert.FromBase64String(s2); //Signature
			string s3 = "0nRuwNEddXwLfXB7pw66G71MS93gW8mNzJ7vuh3Sf4VAEOBfpxtHLCotymv1PoeukxYe31K441Ivq0Pkvx1yZZG4O1KCv3Omdbs7uqzUB4xXHlOub4VsTODzDJ5MWHqlRCB1HHcGjlyT2sVGiovLt0Grvqw5+QXBuinoBY0suX0=";
			string s4 = "AQAB";
			byte[] byte_2 = Convert.FromBase64String(s3);
			byte[] byte_3 = Convert.FromBase64String(s4);
			Class823 @class = new Class823(byte_2, byte_3); //公钥
			@class.method_0(bytes, byte_); 
		}

然后就看见了rsa的指数与那什么值,算了,暴力是唯一出路…
好了升级版本… 试到18.x,这个时期的代码依旧很友好,和12年的差别不大.
到了19.x瞬间不一样了…
以下是19.10的分析过程.
一步一步来走吧,哪里报错停哪里.然后找到这里

	// Token: 0x06004D31 RID: 19761 RVA: 0x002F5DB4 File Offset: 0x002F3FB4
	private object u0002(object[] u0002, Type[] u0003, Type[] u0005, object[] u0008)
	{
		if (u0002 == null)
		{
			u0002 = global::u0008u2002.u0002;
		}
		if (u0003 == null)
		{
			u0003 = Type.EmptyTypes;
		}
		if (u0005 == null)
		{
			u0005 = Type.EmptyTypes;
		}
		this.u0002u2000 = u0008;
		this.u0006u2003 = u0003;
		this.u0003u2002 = u0005;
		this.u0005u2000 = this.u0002(u0002);
		this.u0003 = this.u0008u2001();
		object result;
		try
		{
			u000Eu2006 u000E_u = new u000Eu2006(this.u000E);
			try
			{
				using (this.u0008u2001 = new u0006u2006(u000E_u))
				{
					this.u0006u2000 = false;
					this.u000F = null;
					this.u0005u2001.u0002();
					this.u0006();//这里报错
				}
			}
			finally
			{
				((IDisposable)u000E_u).Dispose();
				...
				...
				...
				...
 

断点下这里,f5 两次后报错,那么f5一次跟进.然后就到这里

private void u0005u2000()
	{
		long num = this.u0008u2001.u0002().u0002u2005u2005u200Bu000E();
		while (!this.u0006u2000)
		{
			if (this.u000F != null)
			{
				this.u0008u2001.u0002().u0002u2005u2005u200Bu0002((long)((ulong)this.u000F.Value));
				this.u000F = null;
			}
			this.u0002();
			if (this.u0008u2001.u0002().u0002u2005u2005u200Bu000F() >= num && this.u000F == null)
			{
				break;
			}
		}
	}

这里会循环N次,虚拟机嘛,一行代码都不止循环一次…this.u0002();这里跟进去看看

	private void u000F()
	{
		long u000F_u = this.u0008u2001.u0002().u0002u2005u2005u200Bu000F();
		int key = this.u0008u2001.u0003u2000();
		u0005u2008.u000F u000F;
		if (!this.u0006u2002.TryGetValue(key, out u000F))
		{
			throw new InvalidOperationException(u0006u200Au2008u2003.u0002(1761139869));
		}
		this.u000Fu2002 = u000F_u;
		u000F.u0003(this.u0002(this.u0008u2001, u000F.u0002.u0003()));
	}

到这里, out u000F得到对应的程序块,所以每次执行的内容都不一样.
然后咋办呢,我用的笨办法,断点在上一个程序块,按F5数次数,按大概70多次报错.
想想早起版本的判断流程,先读值,然后进行判断,那么那个数组值总存在在哪,于是去找了找局部变量值,发现了这个

-		this	{u0005u2008}	u0005u2008
+		u0002u2000	{object[0x00000001]}	object[]
+		u0002u2001	Count = 0x00000000	System.Collections.Generic.Dictionary
+		u0002u2002	{u0006u2006}	u0006u2006
		u0002u2003	null	object
-		u0003	{u0006[0x00000008]}	u0006[]
-		[0]	{u000Eu2004}	u0006 {u000Eu2004}
+		u0002 (u0006)	null	System.Type
		u0002	"Aspose.Total for .NET"	string
-		[1]	{u0006u2002}	u0006 {u0006u2002}
+		u0002 (u0006)	null	System.Type
+		u0002	Count = 0x00000554	object {System.Collections.Hashtable}
-		[2]	{u000Fu2000}	u0006 {u000Fu2000}
+		u0002 (u0006)	null	System.Type
		u0002	0x00000000	int
-		[3]	{u000F}	u0006 {u000F}
+		u0002 (u0006)	null	System.Type
		u0002	true	bool
-		[4]	{u000E}	u0006 {u000E}
+		u0002 (u0006)	null	System.Type
+		u0002	{string[0x00000001]}	System.Array {string[]}
-		[5]	{u000Fu2004}	u0006 {u000Fu2004}
+		u0002 (u0006)	null	System.Type
+		u0002	{2019/1/15 0:00:00}	object {System.DateTime}
-		[6]	{u000Fu2004}	u0006 {u000Fu2004}
+		u0002 (u0006)	null	System.Type
+		u0002	{0001/1/1 0:00:00}	object {System.DateTime}
-		[7]	{u000Fu2004}	u0006 {u000Fu2004}
+		u0002 (u0006)	null	System.Type
+		u0002	{0001/1/1 0:00:00}	object {System.DateTime}

在u0003中找到了熟悉的的东西,这就很nice了.
下面还有2个时间没出来,不用想,一个是允许升级时间,一个是注册码过期时间.
关掉程序重新开,断点老位置,F5疯狂点当u0003[5]的值出来时开始计数,然后点了12下,u0003[6]的值出来了.
重新开,u0003[5]值出来后点11下,然后跟进

	// Token: 0x06004D99 RID: 19865 RVA: 0x002F86D0 File Offset: 0x002F68D0
	private void u0002(int u0002)
	{
		global::u0006 u = this.u000Eu2000();
		if (u is u000Fu2002)
		{
			this.u0003[u0002] = u;
			return;
		}
		this.u0003[u0002].u0006u2005u200Bu0002(u);
	}

然后就到了这里,观察下U的值

-		u	{u000Fu2004}	u0006 {u000Fu2004}
+		u0002 (u0006)	null	System.Type
+		u0002	{2015/12/31 0:00:00}	object {System.DateTime}

满意.把这个值改2099年,程序跑起来了.再观察下生成的xlsx文件及图片,没水印,这应该就没问题了吧.
再试试,断点下在这个程序块,不用那么多下F5了,观察下每次U的值,原来读出来的值都在这里处理一边.

然后看看时间值的类型u000Fu2004,再点进去看,然后就觉得自己特别傻,按F5好玩么…在这个时间类型的构造函数下段就行了嘛…

public u000Fu2004(object u0002)
	{
		if (u0002 != null && !(u0002 is ValueType))
		{
			throw new ArgumentException();
		}
		this.u0002 = u0002;
	}

然后就是改程序了…

public u000Fu2004(object u0002)
	{
		if (u0002 != null && !(u0002 is ValueType))
		{
			throw new ArgumentException();
		}
		if(u0002 != null)
		{
			bool flag = ((DateTime)u0002).Year == 2015;
			if (flag)
			{
				object now = DateTime.Now;
				this.u0002=now;
				return;
			}
		}
		
		this.u0002 = u0002;
	}

IL指令真不熟…加那么几行研究了几个小时… 大概就是vs里照猫画虎写个然后把代码挪这边来.

保存,试试,没问题了.

接着升级版本,到19.2.然后发现,上个版本的方法不行了.郁闷了好久,回头去分析下以前的验证过程

					if (!flag)
					{
						throw new InvalidOperationException("The license is not valid for this product.");
					}
					DateTime t = DateTime.ParseExact("2012.01.16", "yyyy.MM.dd", CultureInfo.InvariantCulture);
					if (t > this.dateTime_0)
					{
						throw new InvalidOperationException(string.Format("The subscription included in this license allows free upgrades until {0}, but this version of the product was released on {1}. Please renew the subscription or use a previous version of the product.", this.dateTime_0.ToString("dd MMM yyyy", CultureInfo.InvariantCulture), t.ToString("dd MMM yyyy", CultureInfo.InvariantCulture)));
					}
					if (DateTime.Now > this.dateTime_1)
					{
						throw new InvalidOperationException("The license has expired.");
					}
					this.enum127_0 = Enum127.const_1;
					Class828.class828_0 = this;
					return;

最后判断时要比较时间啊,那不在它程序里绕就行了,我到system.datetime的比较里下断点就好了嘛.发现自己好聪明哈哈.然而,人家根本不比较…
于是回退到19.1,再在datetime里下断,这次比较了.然后一步一步跟,到了这里 System.Reflection.RuntimeMethodInfo里的系统函数

	// Token: 0x06004654 RID: 18004 RVA: 0x000FFBAC File Offset: 0x000FDDAC
		[SecurityCritical]
		[DebuggerStepThrough]
		[DebuggerHidden]
		private object UnsafeInvokeInternal(object obj, object[] parameters, object[] arguments)
		{
			if (arguments == null || arguments.Length == 0)
			{
				return RuntimeMethodHandle.InvokeMethod(obj, null, this.Signature, false);
			}
			object result = RuntimeMethodHandle.InvokeMethod(obj, arguments, this.Signature, false);
			for (int i = 0; i < arguments.Length; i++)
			{
				parameters[i] = arguments[i];
			}
			return result;
		}

然后看看result以及参数值,明白了.这里下断看看.
升级到19.2,找到UnsafeInvokeInternal,下断

然后就看见了可爱的比较函数,原来转INT32搞了.比较结果是1. 去看看函数的原型,返回值分别可以是1,0,-1
把result的值改改,然后跑起来.程序不报错了.但是打开生成的excel文件,格式错误…这就不好玩了啊…
想了想,应该是它判断了多次.而且不至于傻到只针对这个值判断,那就应该打开了多次文本来比较.
再看一眼虚拟机前的版本,找到这句

			XmlDocument xmlDocument = new XmlDocument();
			xmlDocument.Load(stream_0);
			this.method_2(xmlDocument);

跑system.xml里去下断,这家伙果然开了2次.第二次打开文件后跟进瞧瞧,跟到这里

internal void u0002()
	{
		if (u000Fu2008u2002u2000.u0002 == null)
		{
			return;
		}
		u0003u2002u2002.u000F = null;
		Stream u = new MemoryStream(u000Fu2008u2002u2000.u0002);
		u0006u2005u2002u2002.u0002(0);
		this.u0002(u);
		if (this.u0008 == null)
		{
			return;
		}
		if (u0006u2005u2002u2002.u0005() > 0)
		{
			return;
		}
		Hashtable hashtable = u0003u2002u2002.u0002u2000;
		if (hashtable == null)
		{
			hashtable = u0003u2002u2002.u000E();
			u0003u2002u2002.u0002u2000 = hashtable;
		}
		if (hashtable == null || hashtable.ContainsKey(this.u0005))
		{
			return;
		}
		if (u0006u2005u2002u2002.u0005() > 0)
		{
			return;
		}
		bool flag = false;
		foreach (string text in this.u0002)
		{
			if (text.Equals(u0005u2001u2009u2003.u0002(-1118133675)) || text.Equals(u0005u2001u2009u2003.u0002(-1118133664)) || text.Equals(u0005u2001u2009u2003.u0002(-1118133756)) || text.Equals(u0005u2001u2009u2003.u0002(-1118133720)) || text.Equals(u0005u2001u2009u2003.u0002(-1118133558)) || text.Equals(u0005u2001u2009u2003.u0002(-1118133532)))
			{
				flag = true;
				break;
			}
		}
		if (!flag)
		{
			return;
		}
		string[] array = u0005u2001u2009u2003.u0002(-1118133513).Split(new char[]
		{
			'.'
		});
		string text2 = array[0] + array[1] + array[2];
		if (string.Compare(text2, this.u0008) > 0)
		{
			return;
		}
		if (this.u0006 != null)
		{
			string strA = DateTime.UtcNow.ToLocalTime().ToString(u0005u2001u2009u2003.u0002(-1118133632));
			if (string.Compare(strA, text2) < 0 || string.Compare(strA, this.u0006) > 0)
			{
				return;
			}
		}
		this.u000E = (u0005u2002u2002)1;
		u0003u2002u2002.u000F = this;
	}

这怎么看着那么熟悉啊.比较下我提取出来的虚拟机以前的代码(参考最上面的代码块)
呵呵…换汤不换药.比较函数改到了这里

		if (string.Compare(text2, this.u0008) > 0)
		{
			return;
		}
		


比较字符串大小娘的 .net还有这个操作? 学习了. 继续改.然后跑起来… 这下总算没问题了.

改IL指令的时候才发现上面我搞错的一个地方,2次比较都是system.string.compare…又蠢一回.
在这里下断能准确的找出2次比较.剩下如何改有兴趣的自己折腾吧

升级到最新版本,这个方法依旧奏效,收工

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号