5365bet手机版客户端

【我爱破解】对某软件的逆向分析与注册机编写

【我爱破解】对某软件的逆向分析与注册机编写

对某软件的逆向分析与注册机编写

前言没有注册的情况破解过程查看软件信息反编译出源代码分析源代码编写注册机生成注册码

前言

这个软件会获取计算机的硬件信息(包括:CPU序号、MAC地址、硬盘序列号),然后根据这些硬件信息生成一个序列号。使用者把序列号发给软件提供者,然后提供者将注册码(.lic格式文件)发给使用者,使用者利用注册码即可完成注册。

本文将解决以下问题:

软件如何利用硬件信息生成序列号?(硬件信息与序列号的转换关系)软件如何根据序列号来判断注册码是否有效?如何根据序列号(或者硬件信息)得到注册码?

注意:以下内容将屏蔽敏感信息!本文仅做技术研究!

没有注册的情况

首先运行软件: 软件会直接弹出对话框。 点击“确定”后,点击上方菜单栏中的“注册”,点击“获取硬件信息”,此时会自动打开软件安装目录下的“硬件信息采集工具.exe”。 在界面中可以直接看到这个软件读取到的硬件信息:CPU序号、MAC地址、硬盘序列号。 点击“硬件序号”,就得到了序列号。 正常流程下,我需要把这个序列号发给软件提供者,然后从提供者那里得到注册码,再点击图2中的“注册”。

破解过程

查看软件信息

同样地,使用ExeinfoPe查看这个软件的编写语言,查看是否加壳。 显然,这个软件使用C#语言语言编写,基于.NET框架。

C#这种把源代码编译为中间代码的语言,可以很轻松地从中间代码上反编译出原始代码! 因此,接下来就可以用C#反编译软件ILSpy为所欲为~

反编译出源代码

用ILSpy打开这个软件的主exe程序。 在左边视图中可以看到,这个软件有3个命名空间:Quality.Startup、Quality.Startup.Config、Quality.Startup.Properties。

根据这些命名空间下面的类名,很容易猜到注册功能是在UCRegister这个类中实现的!

接下来就是分析UCRegister类的代码。下面展示实现功能的核心代码:

//流程第1步(笔者注释)

private bool Authorize()

{

try

{

string text = uiTxtRegisterKey.Text;

string text2 = "";

string text3 = "";

//流程第2步(笔者注释)

if (!text.Contains(text3) || !text.Contains(text2))

{

new Form().ShowErrorDialog("硬件序号异常,请在生成硬件序号时拷贝所有字符信息");

return false;

}

//流程第2步(笔者注释):首尾替换

text = text.Replace(text2, "").Replace(text3, "").Trim();

//流程第3步(笔者注释)

text = ExtendedUtils.Decrypt(text.Substring(0, text.Count() - 4), text.Substring(text.Count() - 4));

HardMessage hardMessage = JsonHepler.Json_DeserializeObject(text);

if (hardMessage == null)

{

new Form().ShowErrorDialog("硬件序号异常,请在生成硬件序号时完整拷贝,且不要改动任意字符信息");

return false;

}

EnumHelper.GetEnumDescription(hardMessage.Duration);

if (hardMessage.CPUInfo != HardWareInformation.CPUInfo || hardMessage.DiskNo != HardWareInformation.DiskNo || hardMessage.MacAddress != HardWareInformation.MacAddress)

{

new Form().ShowErrorDialog("硬件信息不匹配");

return false;

}

ServerMessage serverMessage = new ServerMessage();

serverMessage.Clients = 1;

serverMessage.Code = Guid.NewGuid().ToString();

serverMessage.Date1 = DateTime.Now;

switch (hardMessage.Duration)

{

case EDuration.Forever:

serverMessage.Date2 = DateTime.Now.AddYears(100);

break;

case EDuration.OneMonth:

serverMessage.Date2 = DateTime.Now.AddMonths(1);

break;

case EDuration.ThreeMonth:

serverMessage.Date2 = DateTime.Now.AddMonths(3);

break;

case EDuration.SixMonth:

serverMessage.Date2 = DateTime.Now.AddMonths(6);

break;

case EDuration.OneYear:

serverMessage.Date2 = DateTime.Now.AddYears(1);

break;

case EDuration.TwoYear:

serverMessage.Date2 = DateTime.Now.AddYears(2);

break;

case EDuration.FiveYear:

serverMessage.Date2 = DateTime.Now.AddYears(5);

break;

default:

serverMessage.Date2 = DateTime.Now.AddMonths(1);

break;

}

serverMessage.HDMessage = new HardMessage

{

CPUInfo = HardWareInformation.CPUInfo,

DiskNo = HardWareInformation.DiskNo,

MacAddress = HardWareInformation.MacAddress

};

File.WriteAllText(Paths.AppLicfilePath, ExtendedUtils.Encrypts(JsonHepler.Json_SerializeObject(serverMessage)));

if (serverMessage != null)

{

skinLabelLastTime.Text = LimitTime(serverMessage);

}

UIMessageTip.ShowOk("注册完成");

skinLabelLastTime.Visible = true;

uiLabel.BringToFront();

uiLabel.Visible = true;

skinLabelLastTime.BringToFront();

MainForm.Instance.Text = MainForm.RegisteredTitle;

MainForm.Instance.SetStatusMsg(string.Empty);

return true;

}

catch (Exception ex)

{

LogHelper.Error(ex);

}

return false;

}

分析源代码

经分析,读取注册码后的流程为:

用户指定注册码文件路径后,软件调用uiSymbolButton1_Click方法读取注册码文件,得到注册码text。用户点击“注册”按钮,软件调用Authorize方法(该方法就是上面展示的核心代码);如果text中不包含字符串"" 或 “”,则报错。否则,把"" 和 ““都替换成空字符串,也就是删掉text中的”” 和 “”,得到text;调用ExtendedUtils.Decrypt方法对text解密。密文是text的第0位到倒数第5位(text.Substring(0, text.Count() - 4)),解密密钥是text的最后4位(text.Substring(text.Count() - 4))。解密后的明文也存在text中;调用JsonHepler.Json_DeserializeObject方法对text反序列化,得到hardMessage;分别提取hardMessage中的CPUInfo字段、DiskNo字段和MacAddress字段(这些字段代表了注册码中的硬件信息),与本机的实际硬件信息对比,如果全部匹配,则注册码有效!

数据的大致流程图如下(注意:hardMessage的字段除了图中的三个以外,根据反编译出来的源代码可知,还有Clients字段): 以上的流程回答了问题2!

至此,再来回答问题1: 既然软件验证注册码的时候用到Json_DeserializeObject方法来反序列化(字符串→硬件信息) 那么生成注册码的时候就可以用Json_SerializeObject方法来序列化(硬件信息→字符串)!

最后回答问题3: 对于破解者来说,需要写一个程序(或者称为注册机),输入硬件信息(CPU序号、MAC地址、硬盘序列号),输出注册码。 参考上面的流程图,很容易想到,只要把这个流程反过来就行。 既然软件判断注册码是否有效的流程是: (注册码)去掉两个字符串→解密(得到序列号)→反序列化→对比硬件信息

那么破解者的程序流程就应该是: 输入硬件信息→序列化(得到序列号)→加密→加上两个字符串(得到注册码)!!

参考源代码: 软件使用ExtendedUtils.Decrypt方法解密 那么注册机就可以用ExtendedUtils.Encrypt方法加密!

软件使用JsonHepler.Json_DeserializeObject方法反序列化 那么注册机就可以用JsonHepler.Json_SerializeObject方法序列化!

其中要注意的一个小细节是:根据源代码可知,解密用的密文在前,4位长度的解密密钥紧跟着密文后面,密文和解密密钥是不重叠的!密文在前解密密钥在后组成字符串。因此,解密用到的密钥不是固定的,完全就是字符串的最后四位!

而且在ILSpy中继续查看ExtendedUtils.Decrypt方法的实现方法(如下图),发现是使用DES算法加密,这是一种对称加密算法,加密密钥和解密密钥相等,因此加密密钥是可以任意指定4位字符串的!

编写注册机

接下来就可以编写一个注册机了,这也是最激动人心的时候!

与软件相同,注册机也用C#语言写,使用Winform做一个小窗体界面。 输入:硬件信息(包括CPU序号、MAC地址、硬盘序列号)。 输出:注册码文件。

注意:以下只展示核心代码!

首先需要定义一个HardMessage类,作为序列化的依据。

class HardMessage

{

public string CPUInfo { get; set; }

public string DiskNo { get; set; }

public string MacAddress { get; set; }

public int Clients { get; set; }

}

然后就到了注册机的核心部分:

//构造硬件信息变量hardmessage,对hardmessage序列化为SerializeMessage

HardMessage hardmessage = new HardMessage { CPUInfo = MyCPUInfo, DiskNo = MyDiskNo, MacAddress = MyMacAddress, Clients = 1 };

string SerializeMessage = JsonHepler.Json_SerializeObject(hardmessage);

//加密。明文为SerializeMessage,加密密钥为"abcd"(密钥可任意指定一个长度为4的字符串)

//加密后的密文为EncryptMessage

string EncryptMessage = ExtendedUtils.Encrypt(SerializeMessage, "abcd");

//在密文EncryptMessage前加"",后加""

string License = "" + EncryptMessage + "";

//生成注册码文件

StreamWriter sw = new StreamWriter("注册码.lic");

sw.Write(License);

sw.Dispose();

生成注册码

代码写全后,编译运行。

把硬件信息输入后,在注册机所在目录下就生成了“注册码.lic”的文件。 最后,在图2中的界面点击“浏览”,选择这个“注册码.lic”文件,点击注册。。。。。。 Bingo!!