一个.net软件的算法分析
之前给一个群里的朋友做的记录,今天看到有人求破同样的软件但不同的版本,留着也没什么用,发上来,给有需要的人吧,简单的记录,大牛就不用看了
可能会影响一些人的利益,有人提醒我删掉帖子,我把相关的名字改成xx吧
--------------
XXXX程序算法分析简单记录
一、 简单去混淆和定位算法的关键位置
首先打开程序,点帮助里面的注册,输入任意的注册码,提示注册码不对
有了明显提示,对于我这样的菜鸟来说,信心增加了不少。
下面用ILSpy打开安装完的程序xx.exe
任意点一个进去,发现名称全部被是两个随机的字母,这样找关键点,有点头疼,下面用神器de4dot-2.0.3 来处理一下
进入cmd,把De4dot的主程序拖到cmd窗口,空格,然后再把需要处理的xx.exe
拖动进去,按回车,如图:
下面再把处理过的xx用ILSpy打开,
很快可以找到frmRegister这个位置,从名字就可以看到,这里应该就是注册窗口了。
下面来找注册这个按钮事件的相关代码
展开,可以看到frmLoade的事件,但其他的都看不到明显的名称,往下一点找,
可以看到method_0(object,EverArgs):void,看这个事件里面的代码,可以看到类似注册失败的字符串,那应该就是这里没错了
关键位置定位完毕,下面来简单的看看注册部分的代码
二、 分解注册码里面的秘密
本人也是菜鸟,有不对的地方大牛指导一下,免得误导他人
开始是一些判断,
if (Class18.bool_30)
{
this.Close();
}
不知道这是干嘛的,应该是判断是不是注册了吧
if (Strings.Len(Class16.string_0) == 0)
{
Interaction.MsgBox("机器码不能为空,如不能解决,请与XX联系。", MsgBoxStyle.OkOnly, null);
}
这里比较明显,如果传入的参数string-0长度等于0,那就是机器码获取错误了,
继续往下:
string text = Strings.Trim(this.vmethod_0().Text);//获取机器码保存到text
string str = "";
string text2 = Strings.Trim(this.vmethod_6().Text);//获取注册码保存到text2
string text3 = Strings.Mid(text2, 1, 4) + Strings.Mid(text2, 9, 4);
//text3读取注册码的前四位和后四位
string text4 = Strings.Mid(text2, 5, 1);// text4读取注册码里面的第5位
string text5 = Strings.Mid(text2, 7, 1);// text5读取注册码的第7位
分别记下
Text=机器码
Text2=注册码
text3读取注册码的前四位和后四位
text4读取注册码里面的第5位
text5读取注册码的第7位
if (Operators.CompareString(text4, "6", false) == 0 | Operators.CompareString(text4, "7", false) == 0 | Operators.CompareString(text4, "B", false) == 0)
{
text4 = "1";
}
if (Operators.CompareString(text4, "8", false) == 0 | Operators.CompareString(text4, "9", false) == 0 | Operators.CompareString(text4, "C", false) == 0)
{
text4 = "2";
}
比较注册码里面的第五位如果等于6或者7或者B那将注册码的第五位赋值等于1
如果等于8或者0或者C,那就等于2
//==============================
string value = "20" + text4 + Strings.Mid(text2, 6, 1);
看看这句,value等于20+1或者是2(由前面判断得到)+注册码的第六位
组合起来就是201X 202X
那这样就明显了,如果第六位等于3那值应该就是2013或者2023
很熟悉,知道了这代表年,既然有年,那应该就有月吧,继续往下
//=====================
if (Operators.CompareString(text5, "6", false) == 0 | Operators.CompareString(text5, "7", false) == 0 | Operators.CompareString(text5, "B", false) == 0)
//text5等于读取注册码里面的第七位,这里分析可以看到,如果第七位等于6或者等于7或者等于B,那第七位等于1
{
text5 = "1";
}
if (Operators.CompareString(text5, "4", false) == 0 | Operators.CompareString(text5, "5", false) == 0 | Operators.CompareString(text5, "A", false) == 0)
//如果第七位等于4或等于5或等于A,那第七位等于0
{
text5 = "0";
}
string value2 = text5 + Strings.Mid(text2, 8, 1);
//简单的解释这句value2等于第七位加注册码里面的第8位
If text5=1 第8位设置一个2
那就是12
If text5等于0
那就是02
这下看出来了这里是作者为了掩饰,防止别人一眼就看出来到期的时间,加了些混淆的东西,
下面整理一下这部分:
年:等于20+注册码的第5位+第6位
月:等于注册码的第7位+第8位
if (text2.Length == 13 & Operators.CompareString(Strings.Mid(text2, 13, 1), "1", false) == 0)
这里是判断注册码长度是不是13位,第13位是不是1
//---------------------------
继续看下面的判断:
int num;定义两个整数型的变量 年和月
int num2;
try
{
num = Conversions.ToInteger(value);//转换为整数,如果不是数字,那就会出错,返回注册码错误
num2 = Conversions.ToInteger(value2);
}
catch (Exception expr_341)
{
ProjectData.SetProjectError(expr_341);
this.vmethod_2().Text = "注册码不对!";
ProjectData.ClearProjectError();
return;
}
if (num < 2013 | num > 2030 | num2 < 1 | num2 > 12 | num < DateAndTime.Now.Year | (num == DateAndTime.Now.Year & num2 < DateAndTime.Now.Month))
{
this.vmethod_2().Text = "注册码不对!";
}
//这里也比较好看得懂,判断年是不是小于2013或者大于2030,月是不是小于1大于12并判断当前时间是不是超过了注册码的时间,也就是是不是到期了。到期了就返回注册码错误
//
if (this.short_0 == 1)
{
this.method_1(this.vmethod_8(), new EventArgs());
}
else
{
if (Operators.CompareString(this.vmethod_4().Text, "", false) == 0)
{
this.vmethod_2().Text = "您没有输入姓名。";
}
else
{
if (Strings.Len(text3) == 0)
{
this.vmethod_2().Text = "您没有输入注册码。";
}
else
{
//这里也比较容易看懂,判断用户名和注册码是不是输入了
if (Class18.smethod_21(ref text, ref text3) & !File.Exists(Class21.string_8 + "\\syspbaz713.dll"))
{
Class1.smethod_0().Registry.SetValue("HKEY_CURRENT_USER\\Software\\NanfangSoft .net\\PaiBazi713", "Name", this.vmethod_4().Text);
string text6 = Strings.UCase(text3);
Class21.smethod_11(ref text, ref text6, ref value, ref value2);
Class21.bool_0 = true;
this.vmethod_2().Text = "注册成功!点击'退出'。" + str;
}
else
{
this.vmethod_2().Text = "注册码不对!";
}
这里来到真正的判断注册码是不是正确的地方了,
Class18.smethod_21(ref text, ref text3)
这句是关键
Class18.smethod_21(ref text, ref text3)
调用函数smethod_21(机器码,注册码的前面4位和后面4位)
两个参数 机器码,注册码前后四位
关于这个函数,应该就是机器码的对比了,接下来继续分析
//---------------------------
那初步估计,注册码只能输入12位了
//来整理下这段的思路
1、 注册码长度12位
2、 注册码的第5第6等于年的后两位
3、 注册码的第7第8位等于月份
//End
到期时间分析完了,接下来就是注册码的前面4位加上后面四位是干嘛的了
继续。。。
三、 程序是怎样判断机器码是否对应的
Class18.smethod_21(ref text, ref text3
从这里接着看,点击进入.smethod_21
看到下面代码:
传入两个参数string_54等于机器码,string55等于分离后的注册码
if (Operators.CompareString(string_54, "", false) == 0 | Operators.CompareString(string_55, "", false) == 0)
{
result = false;
}
这里好看懂,注册码或机器码其中一个为空,返回False
string text = Class15.smethod_14(ref string_54);
这里又调用了一个函数smethod_14(机器码)
这里传入一个参数,也就是机器码,根据经验,这里肯定是机器码的加密或者解密
,进去看看里面到底干了什么,这里进去看代码像是MD5的加密,可以自己复制代码在C#的工程里面,调用一下就知道了
返回加密后的md5,这里记录一下smethod_14的作用,之后还会用到
///-------------------------
string text = Class15.smethod_14(ref string_54);
也就是说,text等于加密后的MD5
Class16.smethod_5(ref text);
又一个调用,进去看看都干了什么?
if (text.Length > 10)
{
text = Strings.Left(text, 10) + Strings.Right(text, 5);
}
text等于取机器码的左边10位+右边5位
//----------------
string text2 = Class15.smethod_14(ref text);//这句将text进行md5加密
text2等于 机器码MD5加密然后取左边10位+右边5位,再进行md5加密
这样就比较明显了
//======================
Class16.string_1 = Class16.smethod_4(ref text2);//继续分析这个
进smethod—4看看
可以看到这里没有调用其他的子程序,可以直接复制到VS里面,然后直接调用,这里也不太难,取出第8、3、15、12、17、2、17、1、14、13、9、8后面的就不分析了,技术太菜,不好献丑了,直接放到代码里面调用
再返回去看看比较
if (Operators.CompareString(Strings.UCase(Strings.Mid(Class16.string_1, 5, 8)), Strings.UCase(string_55), false) == 0)
{
Class16.string_1 = "a@^*(^*ga$(&%io";
result = true;
}
else
{
result = false;
}
从返回的字符串里面从第5位起读取8个,然后和传入的参数注册码前后4位比较
//End
改分析的也分析完了,最后来整理一下思路吧
四、 整理思路,做算法注册机
1、 注册码的第5第6等于年的后两位
2、 注册码的第7第8位等于月份
3、 将机器码进行MD5加密
4、 加密然后取左边10位+右边5位
5、 再进行一次md5加密
6、 调用直接复制的函数,传入参数md5
7、 返回的字符串从第5位起读取8个
8、 组合分离返回的前4位+年(两位)+月(两位)+返回的后面四位
---------------
论坛排版不太习惯,没显示图,直接上传word文档吧
下载word版本
算法分析记录.doc
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)