本文作为本栏目下原贴 "关于 dnspy破解某收费.net软件注册码的探讨" 的深入研究帖子
原帖地址:
https://www.52pojie.cn/thread-1612175-1-1.html
本帖目的与价值
1.介绍基于.NET的C#窗口程序启动逻辑架构
2.dnspy软件关于IL代码的编辑(对于直接修改代码报错或者有软件大小自效验的程序适用)
3.借鉴学习软件作者的验证程序逻辑
4.借鉴软件作者程序代码中对注册表,表格,加密解密算法代码的学习
关于这个软件分析共分两个章节(爆破与注册机)
本篇内容是注册机篇
爆破篇地址 https://www.52pojie.cn/thread-1614580-1-1.html
注册机篇地址 https://www.52pojie.cn/thread-1614583-1-1.html
概览
注册界面验证逻辑架构:
注册按钮事件中判定逻辑明细btn_active_Click
a.png (57.67 KB, 下载次数: 0)
下载附件
2022-4-1 01:33 上传
将注册码文本框中内容赋值到内部定义变量text
将text内容传递给注册算法的方法SecurityUtil.Verify,并读取返回值(true/false)
false
弹窗(注册码错误);返回
true
弹窗(激活成功)→初始化注册表(建立注册表类)→将text文本写入注册表license键值→弹出重启对话框→
点击确认按钮→软件重启
注册算法逻辑架构Verify
aa.png (71.38 KB, 下载次数: 0)
下载附件
2022-4-1 01:33 上传
`bool flag = rsacryptoServiceProvider.VerifyData(Encoding.UTF8.GetBytes(string.Format("[{0}][{1}][{2}]", networkAdpaterID, SecurityUtil.Encrypt(SecurityUtil.passwd), array[1])), "SHA", signature);`
将注册码用分隔符拆分成数组→获取网络配置信息→导入RSA公钥→将注册码拆分数组第一部分从BASE64还原成字节→效验签名返回值
效验签名方式:[网络信息][加密后的设定密码][拆分后的注册码第二部分],SHA比较,注册码第一部分字节
效验成功返回true,失败返回false
客户端申请注册码的内容分析btn_client_Click
b.png (22.68 KB, 下载次数: 0)
下载附件
2022-4-1 01:37 上传
调用生成公钥和私钥方法:保存私钥文件,保存公钥文件
bb.png (38.62 KB, 下载次数: 0)
下载附件
2022-4-1 01:37 上传
获取注册信息:读取私钥文件;网络配置信息;加密后的设定密码
bbb.png (34.54 KB, 下载次数: 0)
下载附件
2022-4-1 01:37 上传
获取第一个可用网卡的MAC地址,并去空格&减号替换成冒号
bbbb.png (64.18 KB, 下载次数: 0)
下载附件
2022-4-1 01:38 上传
归纳整理:
1.申请信息是由2个分号分割的三部分组成:私钥&MAC&加密信息
2.注册码信息是由1个分号分割的两部分组成:私钥签名&拆分注册码第二部分
3.签名前数据是由三部分组成:[MAC][加密信息][注册码第二部分]
是不是很绕?没错,这也是为什么我花了一天的时间研究注册码算法的原因
需要解决以下问题:
对于以前没有接触过RSA的人来说,以下问题需要解决:
rsacryptoServiceProvider.VerifyData三部分 第一部分很容易知道是未处理的源码,第二部分是加密方式,第三部分什么呢?
经过微软MSDN查找到一个链接
https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.rsacryptoserviceprovider.verifydata?redirectedfrom=MSDN&view=net-6.0
参数
buffer
Byte[]
已签名的数据。
halg
Object
用于创建数据哈希值的哈希算法的名称。
signature
Byte[]
要验证的签名数据。
返回
Boolean
如果签名有效,则为 true;否则为 false。
原来是签名,那什么是签名呢?经过好久的搜索又看到了另一个文章
https://blog.csdn.net/e295166319/article/details/52734608
用RSACryptoServiceProvider签名验签
byte[] messagebytes = Encoding.UTF8.GetBytes("luo罗");
RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider();
string privatekey = oRSA.ToXmlString(true);
string publickey = oRSA.ToXmlString(false);
//私钥签名
RSACryptoServiceProvider oRSA3 = new RSACryptoServiceProvider();
oRSA3.FromXmlString(privatekey);
byte[] AOutput = oRSA3.SignData(messagebytes, "SHA1");
//公钥验证
RSACryptoServiceProvider oRSA4 = new RSACryptoServiceProvider();
oRSA4.FromXmlString(publickey);
bool bVerify = oRSA4.VerifyData(messagebytes, "SHA1", AOutput);
研究了半天终于明白了,原来:
已经签名的数据就是没有经过希哈之前的原数据
那希哈的算法就很明显了:
1.导入私钥数据
2.把需要签名的数据转换成字节数组
3.用SignData方法对字节数组进行希哈运算,并返回希哈值
4.希哈值就是要验证的签名数据
那注册码的格式就是:
从注册信息里面提取第二部分数据作为希哈签名的第一部分
从注册信息里面提取第三部分作为希哈签名的第二部分
希哈签名的第三部分作为注册码的第二部分额外添加
所以:注册码分号分割的第二部分信息是自定义的,可以自己填写
所以注册码的生成代码就是这样:
string RegCode = MsgText.Text; //将客户端注册信息内容复制一份放到RegCode变量中
string[] CodeStr = RegCode.Split(new string[] { ";" }, StringSplitOptions.None); //将RegCode数据用分号拆分成数组
string PrivateKey = CodeStr[0]; //私钥数据
string MacAddr = CodeStr[1]; //网卡MAC地址
string CheckBase64Password = CodeStr[2]; //软件作者联系方式加密后的秘钥
//自定义的加密信息,猜测可能是作者预留的申请人信息,也可能是授权过期日期(原软件代码里面没有使用这个数据(只在验证秘钥的时候用了)
string FreeCodeText = FreeText.Text; //自定义加密信息,只在注册码验证的时候用了
//组合私钥签名需要的数据
string PrivateKeyCode = string.Format("[{0}][{1}][{2}]", MacAddr, CheckBase64Password, FreeCodeText);
//将需要私钥签名的字符串转换成字节数组(因为RSA只能处理byte类型)
byte[] PrivateKeyByte = Encoding.UTF8.GetBytes(PrivateKeyCode); //UTF8编码方式,这个在校验签名的时候用的是UTF8,所以要对应
//
RSACryptoServiceProvider RsaOrder = new RSACryptoServiceProvider();//调用(实例化)RSA签名方法
RsaOrder.FromXmlString(PrivateKey); //读取并写入私钥数据
byte[] SignKeyByte = RsaOrder.SignData(PrivateKeyByte, "SHA"); //生成签名数据
string SignKeyText = Convert.ToBase64String(SignKeyByte);//转换签名数据(因为注册时用了FromBase64String还原方法)
string RegKeyText = string.Concat(SignKeyText, ";", FreeCodeText);//组合用于注册的秘钥数据
KeyText.Text = RegKeyText; //将组合后用于注册的秘钥数据写入软件的注册码文本框中
看到了吗,自始至终原软件内都没有把注册码数据放在内存里面进行比较,而且还有自定义验证信息
所以通过下断点去获取注册码的方式已经被作者封的死死的,对于破解小白来说根本就没有通过断点大法截取秘钥的可能性.
不过通过三天的时间,我弄懂了RSA公钥私钥非对称加密的方法和一些概念,至少以后自己想用RSA去做注册码,会挡住大部分新手
在编写注册机的过程中,我也用到了这个软件里面的一些东西,比如:
注册表的读写(注册码直接写入注册表,无需开启软件)
正则表达式判断注册信息格式是不是正确
调用VB的数据输入框来输入数据
Format格式化数据
DES的加密与解密
内部外调数据的处理
回报
我把我写的注册机的源代码项目文件分享出来,也是给.net逆向的水友们一个学习C#编程开始的范例模板和学习动力
启动验证界面
c.png (12.52 KB, 下载次数: 0)
下载附件
2022-4-1 01:44 上传
注册机主界面
cc.png (62.6 KB, 下载次数: 0)
下载附件
2022-4-1 01:44 上传
生成的注册码进行注册
ccc.png (38.6 KB, 下载次数: 0)
下载附件
2022-4-1 01:45 上传
1.注册机启动时是需要密码的,这个密码需要大家去破解了,哈哈哈,也算是给水友们出的一个题目,共同进步嘛
2.注册机没有加密或者混淆处理过,dnspy打开就行了,密码就写在软件的源代码里面哦
3.注册机的启动密码位置和我以前发过的帖子的分析过程有高相似度,找到了对你以后.net逆向也是提升
4.注册机中应用了很多关于FORM事件的东西,这对大家以后分析大型软件时很有帮助
5.注册机源代码我做了非常详细的注释(注释基本上都快和代码一样多了),新手阅读起来很容易理解
6.注册机就作为交流学习,切勿外传用于商业用途.
7.注册机禁止爆破以后外发,保护本篇文章分析的软件作者利益不受损害.
8.注册机源代码的解压密码和注册机启动密码一样哦!
总结
通过这个软件的代码分析,这个软件中使用的代码可以作为C#软件代码开发初学者的经典程序
软件代码干净整洁,变量使用规整精炼
所调用插件的入口函数清晰,程序架构清晰.
几个问题留在这里,是我在测试中发现的,你们来分析一下
1.为什么将试用期改为10年之后软件直接提示已到期?
2.为什么虚拟机和实体机转换后注册秘钥失效?
3.未经过希哈运算的完整数据格是什么样的?
4.是否在调试的时候可以获取到真正的注册码?
5.32位和64位系统注册表的读取位置是否一样?
6.这个软件在验证方面有哪些不足?
7.以你对这个软件验证授权的方式有什么更改的建议?
原版软件,注册机,注册机CSharp源代码文件下载地址:
http://2950800.ysepan.com/
文件夹访问下载密码:52pojie