0x00:事情的起因是这样的,某风和日丽的上午,某某上班划水中,某群里的某只小伙伴在群里随口问了问有没有办法帮忙找个注册码什么的,然后把注册窗口界面给出来,界面如下:
熟悉的窗口,熟悉的图标,没错是我大C#写的,后续通过闲扯,把程序拿到手后就开始练手了。
0x01:文件打包后还是挺大的,将近50M,不过肯定是有重复的内容,文件结构如下:
东西到手后,某数字就直接提示,XXX有YYY什么的需要注意(请不要吐槽安装了某数字,工作需要),无视中。
虚拟机中直接跑起来,不怕暗桩,目标:服务器端->Service.exe,双击。。Emmm没反应,再来一次,还是没有,这什么情况说好的可以单机离线运行的呢。后来开启任务管理器后发现确实运行了,不过运行一下直接退出。什么鬼情况。
后来问道了某小伙伴说是需要安装SqlServer2005以上的数据库,开什么玩笑,这破东西还用安装数据库还是SqlServer,好麻烦啊,不想安装中。。。
0x02:纠结一段时间后,还是没有安装,跑不起来就跑不起来把,反正有人帮忙测试。直接上dnSpy。
那啥,请问谁给你的勇气不进行混淆的,梁静茹都不敢啊。
先研究下刚刚自动退出是什么情况,看看能不能绕过,等下直接调试。一番折腾后,发现程序启动后就先链接了数据库,没有链接成功就自动退出了。嗯没错,我没有安装数据库肯定嗝屁了。
算了不管先,反正也不想跑起来,直接找刚刚那段文字把。“请将以下字符”,就找找个好了。分分钟定位到方法。好吧看情况【GetHDID】这个类貌似挺重要的。
看样子这个方法就是获取机器码或者XXX之类的 【this.textBox1.Text = GetHDID.md5One();】,从代码分析是获取的CPUId,拿到后进行MD5一次,在和一个固定字符串交叉拼接出来。
[C#] 纯文本查看 复制代码 public static string md5One()
{
ComputerSn computerSn = ComputerSn.Instance();
string text = FormsAuthentication.HashPasswordForStoringInConfigFile(computerSn.CpuID, "Md5");
string text2 = "D3684D241BF1BC14C336A09291C4D56E";
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < text.Length; i++)
{
stringBuilder.Append(text[i].ToString());
stringBuilder.Append(text2[i].ToString());
}
return stringBuilder.ToString();
}
写个小窗口,扔过去跑起来看下是不是获取到的一致。好吧,没有难度就是这样的。
0x03:研究校验算法开始,就在上边说的那个【GetHDID】类中,找到了一个【CheckReg】方法,方法有点长
主要核心部分如下:
[C#] 纯文本查看 复制代码 PassWord passWord = new PassWord();
//判断授权文件是否存在,licenses.bin,这个文件请看第一部分的文件结构目录
bool flag = File.Exists(GetHDID.AppDir + "\\licenses.bin");
if (flag)
{
//以流的形式读取
FileStream fileStream = new FileStream(GetHDID.AppDir + "\\licenses.bin", FileMode.Open, FileAccess.Read);
byte[] array = new byte[fileStream.Length];
BinaryReader binaryReader = new BinaryReader(fileStream);
binaryReader.BaseStream.Seek(0L, SeekOrigin.Begin);
array = binaryReader.ReadBytes((int)binaryReader.BaseStream.Length);
//流文件读取结束
//一开始这部分操作没有看懂,后边才发现是截取了上边byte组中的最后8位
byte[] array2 = new byte[8];
for (int i = array.Length - 8; i < array.Length; i++)
{
array2[i - (array.Length - 8)] = array[i];
}
//对内容进行解码,嗯用的是DES还是DESC来着,反正用的这个东东 [color=#4ec9b0]DESCryptoServiceProvider[/color]
string value = passWord.Decrypt(Convert.ToBase64String(array2), "chenlina");
//这里又重新定义的一个数组,长度的话是上边这个解密出来的
byte[] array3 = new byte[Convert.ToInt32(value)];
//循环赋值中,为啥不考虑CopyTo呢,这样看着一点也不优雅,不过也许是dnSpy的锅。
for (int j = 0; j < array3.Length; j++)
{
array3[j] = array[j];
}
//再次解密
string text = passWord.Decrypt(Convert.ToBase64String(array3), "chenlina");
//文件读取完成
fileStream.Close();
//这里就拿到明文注册码了
string text2 = text.Substring(0, text.IndexOf("----"));
string text3 = GetHDID.md5One();
//冲注册码中截取字符串,和MD5后的CPUId对比,为啥是和CpuId对比,请看上边的内容。
bool flag2 = text.Substring(0, text.IndexOf("----")) == GetHDID.md5One();
//对比通过,其实为了省事完全可以把这部分直接修改,不进行判断,然后在对下边几个参数进行赋值就好了。
//本着咱们是来研究算法的,那么还是按照正常的来把,下边的没啥要解释了,大致就是拿到注册码名称、授权数量、权限等
if (flag2)
{
int num = text.IndexOf("----");
try
{
GetHDID.ClientName = text.Substring(num + 4, text.Length - 6 - num - 10);
}
catch (Exception ex)
{
}
GetHDID.LinesCount = Convert.ToInt32(text.Substring(text.Length - 12, 2));
GetHDID._liness = text.Substring(text.Length - 10);
}
else
{
GetHDID.LinesCount = -1;
}
}
0x04:校验算法完成,接下来是开始写算号器了。上边说到了注册码生成规则是 CpuId转MD5+自己的一个密钥组合成的机器码,然后加一堆的姓名,权限之类的。那么注册码明文规则如下:
{RegCode}----{ClientName}{LinesCount}1111111100,机器码----姓名授权数量权限,那一堆1100的就是权限了,这什么骚操作。明文加密代码如下:
[C#] 纯文本查看 复制代码 //上边说了加解密算法,所以直接网上找了一个进行实现。
//授权码加密,就是机器码
var code = DesHelper.Encrypt(AutCode);
//授权码转换成Byte组
var byCode = Convert.FromBase64String(code);
//授权码Byte数组长度后加密
var codeLength = DesHelper.Encrypt(byCode.Length.ToString());
//授权码长度转Byte组
var byCodeLength = Convert.FromBase64String(codeLength);
//建立二进制文件空间,上述两组文件中有8位空白位置
//这里有个坑,一开始没有注意到两组Byte中有一个8位的空格,所以这里被误导了一下
var retCode = new byte[byCode.Length + 8 + byCodeLength.Length];
//上边就说了用CopyTo看着多舒服
byCode.CopyTo(retCode, 0);
byCodeLength.CopyTo(retCode, byCode.Length + 8);
//后续就是把Byet组写入文件,这个方式N多就不贴代码了
0x05:生成的文件扔过去进行测试。嗯,一次成功
事情一般不会按照你期望的来,这个时候那谁又说,某部分功能没有权限,看能不能处理下。好吧试试呗,在第四步中,注册码明文中有一段1100之类的,按照1启用0不启用的规则,直接把0全部替换成1了。
在走一遍流程,发现没有问题,至此该Erp软件的算法逆推到此结束,后续给上两个成品图片。
|