某.net软件破解笔记
本帖最后由 yypE 于 2015-7-21 22:08 编辑_______________________
TIP.
由于可能涉及到的种种问题,笔者在此就不将该程序发布上来了(软件也不小),仅以此文来交流.NET软件的破解方法。本人小白,如果有错误还请赐教==
_______________________
前言
随着windows系统的更替,PC市面上已经出现了各式各样的应用程序,特别是随着VS高版本的普及以及.net框架的各种方便性,基于.net框架开发的软件也是越来越多,因此笔者写了此篇破解.net程序的文章来献(zhuang)丑(B)。
_______________________
开始
首先安装该软件,安装完成之后来到其主程序目录:
发现一个名为Properties的文件夹,很明显的.net程序特征,在VS2012中新建一个C#项目,就可以在解决方案里看见文件夹“Properties”(以及文件夹“resources”)
当然这并不能作为鉴定其是.net软件的标准方法,使用ExeInfoPE查看其主程序如下:
一般来说,直接鉴定为C#应用程序了(basic一般都不用.net。。。),因此使用爱盘里的NET Reflector v7.6.1.824 Final.rar对其进行反编译
PS.
1.当时分析的时候测试了爱盘里的单文件破解版本.rar]Reflector 6.6.0.30 去更新破解版 By Lkou.rar发现许多时候程序不稳定报错,尤其是在装了reflexil插件之后经常报错,因此推荐使用NET Reflector v7.6.1.824 Final.rar:
2.该程序没有经过混淆处理,因此直接使用反编译工具即可,如果经过加壳处理过了就=。=先不谈了
安装完成后,将爱盘同目录下的Reflexil.1.9.AIO.bin.zip下载,并导入到reflector的插件中,到这里准备工作已经完成
_______________________
观察主程序
运行主程序,立刻弹出明显提示
在选项->授权信息中查看具体信息如下:
途中三处标记为主要需要破解的地方,时间过期后软件将几乎不能使用,底下的两项也同样需要处理,下面开工。。。
_______________________
reflector载入该程序
正常载入,干干净净的没有加过壳的程序==
首先打开“references 引用”分支,将这些以"Flow"开头的dll文件都加载进来(从名字可以看出这些dll与主程序有着直接关系而不是受调用的系统支持库之列),或者干脆直接一次性加载程序目录里头的所有DLL与EXE
这样做的目的是为了更快地能够搜索到关键的函数。
然后,开始一些关键词的搜索,由于是国外软件的中文版本,因此搜索诸如“注册失败”、“您的输入有误”之类的字符串一般都没有用,因为这些字符串一般都是放在某个资源库里头供主程序临时调用的(方便切换各国语言),所以我们不妨来试试一些开发者在写软件的时候很容易给函数起的一些名字,例如: REG/KEY/LICENSE/TYPE/STATUS 等字符串
在搜索时,发现在搜索“license”字符串的时候发现了一个目标:
从名字看来,真是一个惊喜,“Flow授权管理”,瞬间就给我们提供了关键信息,再看其所属命名空间也是该程序的,基本上确定,这个函数一定与该软件的授权管理系统有这密不可分的联系!
双击追踪该函数,并查看其子内容(这不算个函数,应该算个类。。。)
顿时便心花怒放,看这个类里头的函数(检查授权、获取当前授权、获取服务器授权等等),现在可以确定,这就是本次破解的目标了。
其中我们看到一个叫做getTrialRemainingDays的函数,该函数是用来获取试用版剩余天数的,那么如果给他一直返回个相同的值,是不是就永远不会过期的呢?
于是我们双击该函数查看其反汇编代码
可以看到,这个函数只是返回了调用另一个函数的返回值,在这里我们就不继续查看另一个函数的具体内容了,直接上reflexil插件,修改这句代码
在加载了插件之后,右键点击表格,在菜单中选择“replace all with code 将所有用代码替换”(我看不懂这个表格里头的东西,因此就直接用代码替换)
改成:
int _getTrialRemainingDays()
{
return 9999;
}
然后编译,点击OK,接着表格变成了这样(我依然看不懂),不过至少能知道,这一处地方已经修改完毕了,如果不出所料,软件的试用期限将永远不会过期
不过,在实际测试时,笔者发现修改这一处并强制返回并没有作用,于是继续查找,又找到了一处:
这个函数中的代码比较多,并且笔者测试后确定需要修改的目标就是这个函数(位于库FlowUtils):
public static unsafe int GetTrialRemainingDays()
{
string str;
IsolatedStorageFile file;
string[] strArray;
string str2;
IsolatedStorageFileStream stream;
StreamReader reader;
string str3;
DateTime time;
IsolatedStorageFileStream stream2;
StreamWriter writer;
string str4;
string str5;
DateTime time2;
DateTime time3;
int num;
Exception exception;
int num2;
string[] strArray2;
int num3;
DateTime time4;
TimeSpan span;
Label_0000:
try
{
str = "";
file = IsolatedStorageFile.GetStore(5, null, null);
strArray2 = file.GetFileNames("ImageUtils.dll");
num3 = 0;
goto Label_008E;
Label_0023:
str2 = strArray2;
if ((str2 == "ImageUtils.dll") == null)
{
goto Label_0088;
}
stream = new IsolatedStorageFileStream("ImageUtils.dll", 3, file);
reader = new StreamReader(stream);
str3 = reader.ReadLine();
goto Label_0071;
Label_0058:
Console.WriteLine(str3);
str = str + str3;
str3 = reader.ReadLine();
Label_0071:
if (str3 != null)
{
goto Label_0058;
}
reader.Close();
str = CryptoUtils.Decode("9FBABECAA9B31B3EC4AC1829838B789BA518DFE7BA204965", str);
Label_0088:
num3 += 1;
Label_008E:
if (num3 < ((int) strArray2.Length))
{
goto Label_0023;
}
time = DateTime.Now;
if (string.IsNullOrEmpty(prefSavedDate) == null)
{
goto Label_00BC;
}
prefSavedDate = &time.ToString("yyyyMMdd");
goto Label_00D0;
Label_00BC:
DateTime.TryParseExact(prefSavedDate, "yyyyMMdd", null, 0, &time);
Label_00D0:
if (string.IsNullOrEmpty(str) == null)
{
goto Label_013D;
}
str = "ShowImages=" + &time.ToString("yyyyMMdd") + &DateTime.Now.ToString("yyyyMMdd");
str4 = CryptoUtils.Encode("9FBABECAA9B31B3EC4AC1829838B789BA518DFE7BA204965", str);
stream2 = new IsolatedStorageFileStream("ImageUtils.dll", 2, file);
writer = new StreamWriter(stream2);
writer.WriteLine(str4);
writer.Close();
stream2.Close();
Label_013D:
str5 = str.Substring(str.Length - 0x10, 0x10);
&time2 = new DateTime(int.Parse(str5.Substring(0, 4)), int.Parse(str5.Substring(4, 2)), int.Parse(str5.Substring(6, 2)));
&time3 = new DateTime(int.Parse(str5.Substring(8, 4)), int.Parse(str5.Substring(12, 2)), int.Parse(str5.Substring(14, 2)));
log.Debug("INSTALL DATE: " + &time2.ToString());
log.Debug("MAX DATE: " + &time3.ToString());
if ((time3 > DateTime.Now) == null)
{
goto Label_020C;
}
num2 = 0;
goto Label_02A7;
Label_020C:
time3 = DateTime.Now;
str = "ShowImages=" + &time2.ToString("yyyyMMdd") + &time3.ToString("yyyyMMdd");
str = CryptoUtils.Encode("9FBABECAA9B31B3EC4AC1829838B789BA518DFE7BA204965", str);
stream2 = new IsolatedStorageFileStream("ImageUtils.dll", 2, file);
writer = new StreamWriter(stream2);
writer.WriteLine(str);
writer.Close();
stream2.Close();
num = 30;
num = &&time3.Subtract(time2).Days;
num2 = 30 - num;
goto Label_02A7;
}
catch (Exception exception1)
{
Label_0290:
exception = exception1;
log.Error("GetTrialRemainingDays exception", exception);
goto Label_02A5;
}
Label_02A5:
return 0;
Label_02A7:
return num2;
}
仔细观察一下,发现这么多东西实际上就是返回了一个num2,于是果断replace with code
static int GetTrialRemainingDays()
{
return 99999;
}
强制返回一个99999
试用期限的处理到这里就告一段落,接下来要处理的是“最多应答器数量”和“家庭作业下载数量”以及“授权类型”
笔者在现在的这个类中找了一下,并没有发现有相关的设置“最多应答器数量”和“家庭作业下载数量”以及“授权类型”的函数,因此我们又得回到先前的步骤。
继续搜索license,发现有一个与授权窗口相关的类:
其中的这个 SetDefaultTrialValues的内容如下:
public void SetDefaultTrialValues()
{
this.TrialRemainingDays = 30;
this.ExpirationDate = DateTime.Now;
this.ExpirationDate.AddDays((double) this.TrialRemainingDays);
this.SetTrialQuotes();
this.LicensedProduct = LicenseProduct.Flow;
this.LicensedTo = "Demo license";
this.HostName = "Demo license";
this.LicenseType = LicenseType.Trial;
this.LicenseSource = LicenseSource.Host;
this.Status = LicenseStatus.Valid;
this.IsValid = true;
}
可以看到这里决定了demo版本的“licenseto 授权给谁”、“授权使用者是谁”、以及授权的版本、授权的剩余时间
(之前我们已经修改了剩余时间,并强制返回了99999,此处就不作处理了)
同样的,replaceAllWithCode,将上面的代码稍作修改:
this.SetTrialQuotes();
this.set_LicensedProduct( LicenseProduct.Flow);
this.set_LicensedTo( "52PoJie");
this.set_HostName ("yypE's license");
this.set_LicenseType ( LicenseType.Premium );
this.set_LicenseSource ( LicenseSource.Host);
this.set_Status (LicenseStatus.Valid);
this.set_IsValid (true);
修改掉版本类别、两个注册信息后,编译,保存
到这一步,如果不出意外的话(我已经试过了,没意外=.=),时间限制、授权版本已经解决了,接下来就是两个“数量”的问题了。
在现在处在的类中找了找函数,发现一个这样的东西:
。。。。。。replaceAllWithCode
void SetTrialQuotes()
{
this.set_MaxParticipants (0x270f);
this.set_MaxVpads(999);
this.set_MaxHomeworkDownloads(999);
this.set_EnableConstructedQuestionType(true);
this.set_EnableRemoteVpads(true);
}
更改掉两个40为999
_______________________
保存修改
到现在,所有的问题都已经解决了,接下来便是保存处理过的DLL文件,使用插件的保存功能。
保存两个DLL,分别覆盖到主程序目录下即可(在实际处理过程中应该备份源文件)
_______________________
结束
附上破解成功的两张图:
_______________________
后记
最近发现有许多功能强大的境外软件并没有对自身加以强大的保护措施,也没有有意识的防范破解者的破解,想必这与外国的版权法比较健全有所关系。但是,还是应该要加强保护意识,就像如果本次的软件加了混淆(甚至暗桩)之类,破解起来就没这么轻松了。
从这点来看,国产APP还更胜一筹{:17_1078:}
_______________________
声明
本文仅供学习交流,转载还请留名
-yypE
俺骗点CB和热心就闪人,再会各位~{:17_1068:} 我就看看不说话 发表于 2015-7-31 21:09
有没有做逆向的感觉这些代码逆向出来后 放到编译器里面还是不能编译特别是有引用的那种
修改IL代码就可以了,没有混淆的情况下反编译出来整个工程一般也没机会可以直接编译,要修复很多地方 有没有做逆向的感觉这些代码逆向出来后 放到编译器里面还是不能编译特别是有引用的那种 我冲我完全看不懂这一点,要给个分 我也看不懂...太复杂了 {:301_993:}棒棒哒。。。 Reflector就可以直接破解,练习用。 之前有学过.net但最终没坚持下来~有些遗憾,部分能看懂,飘过…… 很好的教程,仔细学习了好几遍,不是很懂,慢慢学吧。 你好 我遇到了一款net程序,在限制功能的地方,也是没有任何关键词提示。该怎么查找相关源代码啊》 没有提示如何找到关键代码啊
你说的的关键词我也搜索了。还是没有结果啊 不错,加精鼓励,期待更多作品!