吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5564|回复: 8
收起左侧

[.NET逆向] .net注册算法分析,悬赏问答区的求助。

[复制链接]
lmze2000 发表于 2017-12-30 19:27
收到悬赏问答区坛友黄小黄的求助,见帖子https://www.52pojie.cn/thread-679092-1-1.html,

试着分析.net的程序,根据原贴,知道是没有混淆的.Net程序,就来尝试一下。

距上次的学习,已经七个月有余,其间忙于工作,逆向一直没有长进。还是作为一名新手,来分析这个程序。

同时也以一名新手的视角来向入门的同道来讲解,希望给大家带来帮助。

本次分析的软件地址:链接: https://pan.baidu.com/s/1mhRXJpI 密码: 5fg9

一、工具推荐,.Net程序的分析利器自然是Dnspy,目前最新的版本是4.5.1,新版本的dnspy对于分析太方便了,也更直观一些。

工具下载地址,见A-New神版的帖子https://www.52pojie.cn/thread-660535-1-1.html

小技巧: 不知道算不算是技巧,只是在使用的过程中,偶然发现的,在使用dnspy分析的时候,随着分析的程序填多,左侧列表会有很多.net的库,

在我们开始分析一个新程序的时候,最好是点开文件-->全部关闭,将列表清空。


二、加载程序,
0错误信息.png

打开注册程序,是根据信息码来计算注册码,我们用dnspy来加载注册器。

2信息码生成.png

Dnspy的方便之处就是可以很容易的查看各函数的源码,其中GetGen是用来生成信息码的函数。

1关键函数.png

而GetInfo是注册码的验证函数。由于没有混淆,所以很容易的根据变量的值来直观的查看代码的运行结果。

二、信息码生成函数

[Asm] 纯文本查看 复制代码
					ManagementBaseObject managementBaseObject = enumerator.Current;
					if (managementBaseObject["signature"] != null)
					{
						string text = managementBaseObject["signature"].ToString() + "c2lf";
						int i = 0;
						int length = text.Length;
						while (i < length)
						{
							char [url=home.php?mod=space&uid=452487]@String[/url] = text[i];
							if (num > 2)
							{
								stringBuilder.Append(".");
							}
							stringBuilder.Append((Strings.Asc(@string) * num - num).ToString());
							num++;
							i++;
						}
					}


此处的算法,tring text = managementBaseObject["signature".ToString() + "c2lf";

将获取的串号与"c2lf"拼接,
对num赋初值2,然后进行循环。
stringBuilder.Append((Strings.Asc(@string) * num - num).ToString());
然后将生成的字串,按位取asc码*num-num,进行计算,并以"."进行连接。
生成第一段信息码,
98.168.200.235.318.336.440.423.490.616.1176.637.1498.1515

[Asm] 纯文本查看 复制代码
foreach (ManagementBaseObject managementBaseObject2 in managementObjectSearcher2.Get())
				{
					string text4 = managementBaseObject2["ProcessorId"].ToString() + "c2lf";
					int l = 0;
					int length4 = text4.Length;
					while (l < length4)
					{
						char string4 = text4[l];
						if (num > 3)
						{
							stringBuilder.Append(".");
						}
						stringBuilder.Append((Strings.Asc(string4) * num - num).ToString());
						num++;
						l++;
					}


此处是进行第二处信息码的计算,与第一段代码计算的过程相同,唯一的区别是num初始化为3
,仍就是按位取ASC码*num-num

以上就是GetGen的生成信息码的过程,所以可以根据以上的信息码,
98.168.200.235.318.336.440.423.490.616.1176.637.1498.1515-195.276.340.390.483.520.621.690.517.564.611.700.705.848.1122.900.1862.980.2247.2222
,可以根据这个通过以上的逆运算,得到用于生成注册码的两个参数,,

[Asm] 纯文本查看 复制代码
    Arr = Split(Mw, "-")
    Tmp1 = Split(Arr(0), ".")
    Tmp2 = Split(Arr(1), ".")
    For i = 0 To UBound(Tmp1)
        Jm = (Tmp1(i) + i + 2) / (i + 2)
        Js1 = Js1 + Jm
    Next
    Str = Js1 & "c2lf"

此处为VBA的逆向参数的示例代码

三、GetInfo注册程序

我们现在来看看GetInfo注册验证的过程。

[Asm] 纯文本查看 复制代码
		public static bool GetInfo(string Key, ref int WMin, ref int WMax, ref int HMin, ref int HMax)
		{
			string[] array = Key.Split(new char[]
			{
				'-'
			});
			if (array.Count<string>() != 4)
			{
				WMin = 1;
				WMax = 1;
				HMin = 1;
				HMax = 1;
				return false;
			}
			object[] array2 = new object[4];
			int num = 0;
			do
			{
				array2[num] = Conversion.Val(array[num]);
				num++;
			}
			while (num <= 3);
			ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
			ulong num2;
			try
			{
				ManagementObjectCollection.ManagementObjectEnumerator enumerator = managementObjectSearcher.Get().GetEnumerator();
				if (enumerator.MoveNext())
				{
					ManagementBaseObject managementBaseObject = enumerator.Current;
					if (managementBaseObject["signature"] != null)
					{
						string text = managementBaseObject["signature"].ToString() + "c2lf";
						int i = 0;
						int length = text.Length;
						while (i < length)
						{
							char @string = text[i];
							num2 = Convert.ToUInt64(decimal.Add(new decimal(num2), new decimal(Strings.Asc(@string))));
							i++;
						}
					}
					else if (managementBaseObject["serialnumber"] != null)
					{
						string text2 = managementBaseObject["serialnumber"].ToString() + "c2lf";
						int j = 0;
						int length2 = text2.Length;
						while (j < length2)
						{
							char string2 = text2[j];
							num2 = Convert.ToUInt64(decimal.Add(new decimal(num2), new decimal(Strings.Asc(string2))));
							j++;
						}
					}
					else
					{
						string text3 = "479583c2lf";
						int k = 0;
						int length3 = text3.Length;
						while (k < length3)
						{
							char string3 = text3[k];
							num2 = Convert.ToUInt64(decimal.Add(new decimal(num2), new decimal(Strings.Asc(string3))));
							k++;
						}
					}
				}
			}
			finally
			{
				ManagementObjectCollection.ManagementObjectEnumerator enumerator;
				if (enumerator != null)
				{
					((IDisposable)enumerator).Dispose();
				}
			}
			ulong num3 = 1UL;
			string text4 = num2.ToString() + "c2lf";
			int l = 0;
			int length4 = text4.Length;
			while (l < length4)
			{
				char string4 = text4[l];
				num3 = Convert.ToUInt64(decimal.Multiply(new decimal(num3), new decimal(Strings.Asc(string4))));
				l++;
			}
			ManagementObjectSearcher managementObjectSearcher2 = new ManagementObjectSearcher("SELECT * FROM Win32_Processor");
			ulong num4;
			try
			{
				foreach (ManagementBaseObject managementBaseObject2 in managementObjectSearcher2.Get())
				{
					string text5 = managementBaseObject2["ProcessorId"].ToString() + "c2lf";
					int m = 0;
					int length5 = text5.Length;
					while (m < length5)
					{
						char string5 = text5[m];
						num4 = Convert.ToUInt64(decimal.Add(new decimal(num4), new decimal(Strings.Asc(string5))));
						m++;
					}
				}
			}
			finally
			{
				ManagementObjectCollection.ManagementObjectEnumerator enumerator2;
				if (enumerator2 != null)
				{
					((IDisposable)enumerator2).Dispose();
				}
			}
			ulong num5 = 1UL;
			string text6 = num4.ToString() + "c2lf";
			int n = 0;
			int length6 = text6.Length;
			while (n < length6)
			{
				char string6 = text6[n];
				num5 = Convert.ToUInt64(decimal.Multiply(new decimal(num5), new decimal(Strings.Asc(string6))));
				n++;
			}
			if (Operators.ConditionalCompareObjectEqual(Operators.ModObject(array2[0], num2), 0, false) && Operators.ConditionalCompareObjectGreater(Operators.IntDivideObject(array2[0], num2), 0, false) && Operators.ConditionalCompareObjectEqual(Operators.ModObject(array2[1], num3), 0, false) && Operators.ConditionalCompareObjectGreater(Operators.IntDivideObject(array2[1], num3), 0, false) && Operators.ConditionalCompareObjectEqual(Operators.ModObject(array2[2], num4), 0, false) && Operators.ConditionalCompareObjectGreater(Operators.IntDivideObject(array2[2], num4), 0, false) && Operators.ConditionalCompareObjectEqual(Operators.ModObject(array2[3], num5), 0, false) && Operators.ConditionalCompareObjectGreater(Operators.IntDivideObject(array2[3], num5), 0, false))
			{
				WMin = Conversions.ToInteger(Operators.IntDivideObject(array2[0], num2));
				WMax = Conversions.ToInteger(Operators.IntDivideObject(array2[1], num3));
				HMin = Conversions.ToInteger(Operators.IntDivideObject(array2[2], num4));
				HMax = Conversions.ToInteger(Operators.IntDivideObject(array2[3], num5));
				return true;
			}
			WMin = 1;
			WMax = 1;
			HMin = 1;
			HMax = 1;
			return false;
		}



public static string GenToKey(string Key, int WMin, int WMax, int HMin, int HMax)
此处的函数,有几个参数,一个是Key,就是我们的假码,以及WMin、WMax、HMin、HMax,这几个参数,分别表示水平、垂直画面。
该条函数的验证过程为,
num=2的参数用来计算水平画面的数量,Num=3的参数用来计算垂直画面的数量。

[Asm] 纯文本查看 复制代码
		{
			string[] array = Key.Split(new char[]
			{
				'-'
			});
			if (array.Count<string>() != 4)
			{
				WMin = 1;
				WMax = 1;
				HMin = 1;
				HMax = 1;
				return false;
			}
			object[] array2 = new object[4];
			int num = 0;
			do
			{
				array2[num] = Conversion.Val(array[num]);
				num++;
			}
			while (num <= 3);

此处将假码split进数组Array2,用于和计算出来的真码进行对比。

[Asm] 纯文本查看 复制代码
					ManagementBaseObject managementBaseObject = enumerator.Current;
					if (managementBaseObject["signature"] != null)
					{
						string text = managementBaseObject["signature"].ToString() + "c2lf";
						int i = 0;
						int length = text.Length;
						while (i < length)
						{
							char @string = text[i];
							num2 = Convert.ToUInt64(decimal.Add(new decimal(num2), new decimal(Strings.Asc(@string))));
							i++;
						}
					}


在这里将参数1与"c2lf“拼接,,在此处,进行按位转换成Asc码,然后累加,相加后得出的值,我们称为真码1,也即用来控制水平最小画面的参数。

[Asm] 纯文本查看 复制代码
			ulong num3 = 1UL;
			string text4 = num2.ToString() + "c2lf";
			int l = 0;
			int length4 = text4.Length;
			while (l < length4)
			{
				char string4 = text4[l];
				num3 = Convert.ToUInt64(decimal.Multiply(new decimal(num3), new decimal(Strings.Asc(string4))));
				l++;
			}


水平画面的最大数量,计算过程是用真码1&"c2lf",这个字符串,,该计算过程为,按位取字符串的Asc码然后相乘,
得出的值我们称为真码2.

至此,已经完成水平画面的2个真码的计算。
下面是垂直画面的真码计算过程,与水平的画面一样,但是使用的参数是,信息码的第2部分。

[Asm] 纯文本查看 复制代码
		if (Operators.ConditionalCompareObjectEqual(Operators.ModObject(array2[0], num2), 0, false) && Operators.ConditionalCompareObjectGreater(Operators.IntDivideObject(array2[0], num2), 0, false) && Operators.ConditionalCompareObjectEqual(Operators.ModObject(array2[1], num3), 0, false) && Operators.ConditionalCompareObjectGreater(Operators.IntDivideObject(array2[1], num3), 0, false) && Operators.ConditionalCompareObjectEqual(Operators.ModObject(array2[2], num4), 0, false) && Operators.ConditionalCompareObjectGreater(Operators.IntDivideObject(array2[2], num4), 0, false) && Operators.ConditionalCompareObjectEqual(Operators.ModObject(array2[3], num5), 0, false) && Operators.ConditionalCompareObjectGreater(Operators.IntDivideObject(array2[3], num5), 0, false))


这里有2个验证过程,分别是用Equal函数、mod取余,也就是在这里,假码是真码的整倍数,取余的结果就为0,equal函数判断取余的结果是否等于0,来完成验证过程。
并且在这里,只要是真码的整数倍,也就控制了画面的数量。

四、注册机部分

[Visual Basic] 纯文本查看 复制代码
Sub Decent()
    Dim Mw, Tmp, Jm, Str, Js, Arr
    Dim Js1, Js2, Js3, Js4
    Dim Hmin, Hmax, Wmin, WMax, DeKey
    
    Mw = Application.InputBox("请输入信息码", "注册机")
    Hmin = Application.InputBox("请输入最小水平画面数量", "设置水平画面", 1)
    Hmax = Application.InputBox("请输入最大水平画面数量", "设置水平画面", 1)
    Wmin = Application.InputBox("请输入最小垂直画面数量", "设置垂直画面", 1)
    WMax = Application.InputBox("请输入最小垂直画面数量", "设置垂直画面", 1)
    
Arr = Split(Mw, "-")
    Tmp1 = Split(Arr(0), ".")
    Tmp2 = Split(Arr(1), ".")
    For i = 0 To UBound(Tmp1)
        Jm = (Tmp1(i) + i + 2) / (i + 2)
        Js1 = Js1 + Jm
    Next
    Str = Js1 & "c2lf"
   
    Js2 = 1
    For i = 1 To Len(Str)
        Js2 = Js2 * Asc(Mid(Str, i, 1))
    Next
    For i = 0 To UBound(Tmp2)
        Jm = (Tmp2(i) + i + 3) / (i + 3)
        Js3 = Js3 + Jm
    Next
    Str = ""
    Str = Js3 & "c2lf"
    Js4 = 1
    For i = 1 To Len(Str)
        Js4 = Js4 * Asc(Mid(Str, i, 1))
    Next
    DeKey = Js1 * Hmin & "-" & Js2 * Hmax & "-" & Js3 * Wmin & "-" & Js4 * WMax
    MsgBox "序列号: " & DeKey, vbOKOnly, "Finger"
    [a2] = DeKey
End Sub


根据上面的分析,用VBA写出的注册机,写的比较乱。。

注册机.PNG


写的有点乱,,希望对新人有用。



免费评分

参与人数 8威望 +1 吾爱币 +19 热心值 +7 收起 理由
Sound + 1 + 8 + 1 已经处理,感谢您对吾爱破解论坛的支持!
Poner + 6 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Mr_Beans + 1 + 1 谢谢@Thanks!
zjjyl + 1 + 1 谢谢@Thanks!
kk1212 + 1 谢谢@Thanks!
唯一丶 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
610100 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
sinceret + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

teolinkiat 发表于 2017-12-30 19:34
谢谢楼主分享。
吾爱阿伤 发表于 2017-12-30 20:10
唯一丶 发表于 2017-12-31 00:23
.Net入门都还没学会,硬着头皮看,感谢楼主分享。
kk1212 发表于 2017-12-31 00:38
大神 net逆向分析的很详细了
mujin88888 发表于 2017-12-31 14:38
看看支持下
黄小黄 发表于 2017-12-31 23:51
感谢详细分析,本来还想要不要回帖问一下计算部分怎么解读呢,您这讲解的真的是太详细了。
对于编程方面一丢丢基础的我,看了几个你的帖子,现在已经可以慢慢尝试分析软件的整个运算过程了,dnSpy这个软件真的是太厉害,表示完全不会用od,主要是看不懂,只能死记硬背,并不知道软件运行的是什么,跳的是什么。但是通过dnSpy可以完整的看到整个软件的运行流程,一步一步往下走,就像在回答问题闯关一样,回答错了就错误,停下来。回答对了就继续往前走。
身为小白,感觉你这几个对于.net软件的分析实在是太实用了。最重要的是没有给人一种云里雾里的感觉,而是能把对错和原因看在眼睛里的分析,不仅仅是教程,还是动力~
哈哈,感谢~
stenley123 发表于 2018-1-1 17:28 来自手机
混淆过的,应该就没办法了吧,不过还是学习了
jkoxo123 发表于 2018-5-1 10:41
你好,能帮我看看一个.NET的软件吗?QQ:1247221827 有偿的
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-17 04:53

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表