是个人都能破解之终结NhibernateProfiler-分析篇
当然这个题目的确是稍稍使用了一些夸张的修辞手法。但是只要思路对头,再加上点运气,破解也并不是如你想象中的那般神秘。
为何突然想写这方面的东西呢,那是因为三年前,我曾在CSDN的资源上发布了一个自己写的NHPorfilerCrack,当时写这个工具是为了破解Nhibernate Profiler V1.0(Nhibernate Profiler是一个调试nhibernate的工具),今天在查看资源时又看到了这个工具,于是就把它下了下来,想看看最新版本的Nhibernate Profiler 它还能不能破解了,于是乎到Nhibernate Profiler 的官网(http://www.nhprof.com)下了最新版,目前是2.0 。 试后,发现已经无法破解了,不过这倒把我的胃口调上来了,所以就兴起了再把这个版本破解的想法,俺是那种有了想法,不立即实施觉都睡不着的人,所以既然有了念头,自然马上就要行动了。由于.NET的先天不足,破解它就象俗话说的“比捏死一支蚂蚁还容易”。当然这话也是稍稍使用了一些夸张的修辞手法的,但确实相对于那些加了一些变态壳又加了N多陷阱的WIN32来说,破解它的确是容易多了,尤其是象Nhibernate Profiler 这种没进行什么保护的程序,所以用了小半个小时就把他给搞定了。 正好最近又写了一个[屌丝的逆袭]系列,破解嘛勉强也是适合这个主题的,再加上这次破解的目标是一个真实的软件,而过程呢又简单到非常适合作为一个示范的例子, 于是就有了写这个东西的想法。
俺是个有版权意识的人,_^...…… 所以先发表一些免责声明 :1.破解后的程序没有用于商业目的 2.本文仅是交流技术,使用本文的方法破解后的nhibernate profiler也禁止用于商业目的 3.使用本文附录提供的程序及源码破解的nhibernate profiler也禁止用于商业目的。 4.如发觉nhibernate profiler好用,请支持正版,鉴于天朝伟大的对美元升值政策,使得原来每年需要2000+RMB的东西,现在只需要998就可以搞定了哦, 饿,错了,是1668就可以搞定了哦。
OKAY…… 现在可以开始了。
虽然写了免责声明,但这个主题还是有些敏感的,毕竟我们都是搞程序的,在一个全是程序员的地方,大谈破解,多少还是感觉有点异怪的。当然,我们不是为了破解而破解,所谓知破方能知防,学破解的目的不是为了破,而是为了知道破的手法,这样才能更好的防守,以使得你的程序更加安全。(话外音:我日,居然还真能找出来这么一条冠冕堂皇的理由)
话外音直接忽略,不管你信不信,反正我是信了。 下面我们继续 。
破解.net , reflector自然是必备的神器,如果程序没有做过啥太变态的处理,源代码是可以直接还原出来的,虽然还原成一个完整且能编译通过的工程,复杂度还是有的,但在这里我们只是为了破解,并不是为了继续完善他的代码,所以并不需要还原整个工程,也不需要编译通过,只要能看到文件的源代码就行了,看着源代码研究破解方法,还有比这更爽的事情嘛 :) 所幸nhibernate profiler就是那种基本没做啥处理的软件。
附录的根目录下有个NHProf文件夹,这个就是我从官网上下的2.0版的Nhibernate Profiler,如果你们要照着做的话,最好使用这个程序,不要直接从官网下载,因为官网是在不断升级的,而下面我们写的一切都是基于现在的这个版本也就是2.0的。
OKAY,打开NHProf文件夹,你会发现根目录有一个 NHProf.exe 这个就是Nihibernate Profiler的主程序,双击打开它。运行结果如下
中间弹出了一个Requires a valid license (需要一个有效的许可证)的提示框,然后点击Browser for your license(浏览你的许可证),选择一个合法的许可证,点击打开,这样就可以激活使用了,当然如果你没有许可证的话,就只能点叉了,占击叉后,程序就自动退出了。
在他的官网上可以申请一个30天试用的许可(功能是全的,但只能用30天),我们先申请一个然后导入看一看运行后是什么样子(注:NHProf根目录下的license.xml就是我申请的,你们就不用再申请了)。
上面显示了,Connected, 30 days remaining (还剩30天)
OKAY,现在我们把系统日期往后调到2013-8-1号,这相对于今天2013-4-4号已经远远超过30天了。
再运行一次程序看看。
提示程序只能用到 2013/5/4 号。要求重新导入许可,如果点击关闭,这个程序就退出了。下面我们就来分析如何绕过这个日期限制 。 分析前我先看一看破解后的样子。
可以看到,已经没有过期提示了,上面也变成了Connected -89 days remaining 。 :)
OKAY,下面开始正式的分析过程。
分析前,先备份一份,这是个好习惯,因为破解经常是破坏性的, 备好后,打开reflector.exe(附录的Tools文件夹下有此工具,如果提示升级,必须升级,否则用不了)。 File-->Open 选择NHProf文件夹根目录下的那个nhprof.exe 。
这时候Reflector左边的 树形列表上多了一个 NHProf……如下图所示
没有红字提醒,说明,Nhprof.exe并没有加壳,也没有进行错误元数据处理,这是我们最想看到的结果 :)
展开
看到选中的那个Main方法了吗 ? 这个就是整个程序的入口函数。分析就从这里开始,然后顺着这根藤就可以摸到我们要的瓜了 。
看Reflector右边Disassemble窗口反编译出来的代码
[STAThread]
public static int Main(string[] args)
{
if (!AssertAllFilesPresent())
{
return -3;
}
if (!AssertValidRuntimeVersion())
{
return -4;
}
Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
XmlConfigurator.Configure();
WarmUpSqlParserOnBackgroundThread();
EnsureReferencesToHibernatingRhinoProfilerClientGoesToThisAssembly();
return StartupParser.ParseCommandLineArguments(args).Execute();
}
最后一句StartupParser.ParseCommandLineArguments(args).Execute();看起来比较靠谱,就从他开始,StartupParser.ParseCommandLineArguments(args)返回的是一个IStartupCommand的接口,所以直接在Execute上面点击(如果你导出了工程,然后用VS打开的话,就是右键go to definition(转到定义)),只会转到接口的声明里,是到不了具体的实现类的,所以我们要先看一看StartupParser.ParseCommandLineArguments(args)返回的究竟是那个类的实例。
Disassemble窗口里点击ParseCommandLineArguments 。显示了ParseCommandLineArguments方法反编译的结果。
public static IStartupCommand ParseCommandLineArguments(string[] args)
{
IStartupCommand command;
try
{
CurrentArguments = new ProfArguments();
if (!Parser.ParseArguments(args, CurrentArguments, new ErrorReporter(StartupParser.WriteLineToConsole)))
{
WriteLineToConsole(Parser.ArgumentsUsage(typeof(ProfArguments)));
return new ExitCommand();
}
if (CurrentArguments.GuiMode)
{
return new SilverlightGuiCommand();
}
if (CurrentArguments.CmdLineMode)
{
return new CommandLineCommand(CurrentArguments.SpecifiedPortOrDefaultPort, CurrentArguments.File, CurrentArguments.InputFile, CurrentArguments.ReportFormat);
}
if (CurrentArguments.Shutdown)
{
return new ShutdownCommand(CurrentArguments.SpecifiedPortOrDefaultPort);
}
command = new SilverlightGuiCommand();
}
finally
{
FreeConsoleIfNeeded();
}
return command;
}
看来没有比SilverlightGuiCommand 这个更靠谱的了 ,现在就假定StartupParser.ParseCommandLineArguments(args)返回的就是这个类的实例,那么StartupParser.ParseCommandLineArguments(args).Execute() , 执行的自然是这个类的Execute方法了。看下图
可以看到 SilverlightGuiCommand 这个类在HibernatingRhinos.Profiler.Client.Startup 命名空间下,下面来看看 Execute 方法里 的代码(代码有点多,只留最重要的部分) ,
public int Execute()
{
…
try
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
OutOfProcessClientService service = new OutOfProcessClientService();
service.Initialize();
using (TrayIcon icon = new TrayIcon())
{
icon.OnClose = (Action) Delegate.Combine(icon.OnClose, new Action(service, (IntPtr) this.Close));
icon.Display();
service.StartWithOutOfProcessListener(StartupParser.CurrentArguments.ContextFile);
}
}
catch (Exception exception)
{
...
return 1;
}
return 0;
}
仍然找最靠谱的那句,红字的那句看到了没,基本上就是他了,因为有Listener字样(一定要锻炼这种感觉能力)。因为很多的验证程序为了定时的进行提醒,都采用Listener的方式。进去看一看(在这个方法上点击就可以了) 。这个方法是OutOfProcessClientService类里的方法。
从上图也可以看到OutOfProcessClientService类在HibernatingRhinos.Profiler.Client.Host 命名空间下,我们来看一下StartWithOutOfProcessListener方法的代码。
public void StartWithOutOfProcessListener(string contextFile)
{
base.Start();
Thread thread2 = new Thread(new ThreadStart(OutOfProcessClientService.SelfVerification));
thread2.IsBackground = true;
Thread thread = thread2;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
this.AttemptToStartBackEnd(base.backendBridge);
NonAdminHttp.EnsureCanListenToWhenInNonAdminContext(0x4498);
this.listener.Prefixes.Add("http://+:" + 0x4498 + "/");
ClientService.Log.Debug("Attempt to start HTTP listener");
this.listener.Start();
ClientService.Log.Debug("HTTP listener started successfully");
base.profilerAutoUpdate.Configure();
base.licensingService.OnLicenseValidated += new Action(this, (IntPtr) this.
if (!string.IsNullOrEmpty(contextFile))
{
Guid guid = base.backendBridge.StartLoadFromFile(contextFile);
OpenFileInfo info2 = new OpenFileInfo();
info2.FileName = contextFile;
info2.Token = guid;
OpenFileInfo openFileInfo = info2;
base.backendBridge.Notifications.RaiseBringApplicationToFront(openFileInfo);
}
FileAssociation.SetupAssociation();
this.HandleRequests();
Application.Idle += new EventHandler(this.OnStartUp);
Application.Run();
UpdateManager.Instance.Abort(true);
try
{
ProfilerAutoUpdate.ApplyUpdates(base.applyUpdate);
}
catch (LicenseUpgradeRequiredException exception)
{
ClientService.Log.Info("you need to upgrade your license.", exception);
}
UpdateManager.Instance.CleanUp();
if (base.restart)
{
SilverlightLauncher.GetSiverlightProcess().Kill();
Application.Restart();
}
}
有没有看到上面代码里那个又大又黑又粗(好邪恶的描述)而且异常不协调的licensingService ,出现了这样命名的东西,基本上你就得眼睛一亮了。base.licensingService ,说明这个变量是在父类里定义,我们点一下他,会发现定位到的地方只是一个它的一个声明,
protected LicensingService licensingService;
并没有实例化的语句,所以从习惯上分析应该是在构造函数里对它进行实例化了,本来找到实例化的地方对于我们的分析来讲意义不是很大,因为这里类名已经出现了,知道了类名我们就可以继续往下跟踪分析了,但是有时候在这种不是很麻烦的地方深入的跟一跟,会有意外的收获:) 另外在找实例化的过程中,也会涉及一些知识点,所以我们还是额外的去找一下这个实例化的地方吧 :)
在Reflector里 .ctor 命名的函数 就是构造函数了。
先看OutOfProcessClientService的构造函数,没啥发现,再看一下OutOfProcessClientService的定义
public class OutOfProcessClientService : ClientService
所以父类就是ClientService,点击ClientService。我们发现已经进入另外一个Assembly(HibernatingRhinos.Profiler.Client.Host.dll)里了。这个dll文件就在NHProf文件夹的根目录下
看一下.ctor 也没啥发现。不过这时候你会发现在.ctor下面一点有一个函数Initialize,
这名字取的,让你不得不看一下了
public void Initialize()
{
BackendBridge bridge = new BackendBridge();
bridge.Configuration = UserPreferencesHolder.UserSettings.Configuration;
this.backendBridge = bridge;
this.queryPlanService = new QueryPlanService();
this.queryResultService = new QueryResultService();
this.licensingService = new LicensingService();
this.profilerAutoUpdate = new ProfilerAutoUpdate(UserPreferencesHolder.UserSettings);
}
红字那一排,看到了没。就是实例化的地方。
OKAY,现在成功的分析到了 HibernatingRhinos.Profiler.Client.Host.dll 里。这是很关键的一个突破,这时候,我们看一下HibernatingRhinos.Profiler.Client.Host.dll 的结构。
着重看一下上图中选中的那一排,看到这样一个命名空间难道你没有冲动想展开来看看具体有些啥嘛。展开
看到上图中选择的那个AbstractLicenseVlidator类了吗,在这个类里有一个方法 IsLicenseValid() 。
还有比看到这样的名字更让人鸡动的吗?我想不用讲也知道是什么意思了吧。
看一下这个方法的代码
1 private bool IsLicenseValid()
2 {
3 bool flag2;
4 try
5 {
6 bool flag;
7 if (!this.TryLoadingLicenseValuesFromValidatedXml())
8 {
9 Logger.WarnFormat("Failed validating license:\r\n{0}", this.License);
10 return false;
11 }
12 if (!this.licenseInfoLogged)
13 {
14 Logger.WarnFormat("License expiration date is {0}", this.ExpirationDate);
15 this.licenseInfoLogged = true;
16 }
17 if (this.LicenseType == LicenseType.Subscription)
18 {
19 flag = this.ValidateLicense();
20 }
21 else
22 {
23 flag = DateTime.UtcNow < this.ExpirationDate;
24 if (flag)
25 {
26 flag = this.ValidateLicense();
27 }
28 }
29 if (!flag)
30 {
31 throw new LicenseExpiredException("Expiration Date: " + this.ExpirationDate);
32 }
33 this.ValidateUsingNetworkTime();
34 flag2 = true;
35 }
36 catch (RhinoLicensingException)
37 {
38 throw;
39 }
40 catch (Exception)
41 {
42 flag2 = false;
43 }
44 return flag2;
45 }
到这里,我们就得插一句了,破解是不同于代码分析的,代码分析的过程中不能错过任何一个方法和函数,必须保证所有的方法都能串起来,这样才能完整的还原出程序的框架或思路,而破解却不需要这样做,的确象一开始那样一个方法一个方法的跟踪和分析,肯定也能定位到这里,但现在我们已经看到了这样一个命名空间,和这样一个类,以及这样一个方法,那么在破解里,就已经没有必要再继续一个方法一个方法的一点不落的跟踪下去了,现在完全可以假定,这个方法就是关键破解点,然后下一步去验证它就可以了,如果发现不是,就再顺着刚才的思路再一个方法一个方法的继续跟踪和分析,但是如果这个就是关键破解点的话,那么破解就可以直接完成了。后面也证实了这的确是个关键破解点。
OKAY,分析到这里,我们已经可以确定一下破解的思路了,只要让IsLicenseValid这个方法,永远返回true,不就可以实现无论如何许可都有效了吗,那么怎么样才能让其永远返回true呢,仔细分析一下上面的代码可知,其中红字的那几处是关键,只要把这几处去掉,那么,如果不发生意外的话都会执行到代码里的34行也就是flag2 = true;如此当return flag2时不就是永远返回true了吗。(逻辑上并不是太完美,但实际运行中是没有出现问题的:) )
OKAY,分析的过程到此就算完成了,下面就是验证分析结果的时候了。
当然要想验证分析结果,还得解决一个问题,那就是如何去掉这几处代码,看起来最简单的方法,就是注释掉这几处代码,然后重新编译,但前面我们提过了,用Reflector反编译出来的工程,要想重新编译通过是件相当耗时间和体力的事,所以我们肯定不会去干这种蠢事的。
那么不重新编译的情况下,如何去掉这几处代码呢,其实并不麻烦,直接改二进制文件就可以了。我们晓得在可执行的二进制文件里这段代码肯定是被被编译成指令进行存储的,所以我们只要找到这段代码被编译成指令后在二进制文件中的位置,然后直接把他们全部清0就可以了。当然.NET存储的并不是机器指令,而是IL指令,所以要查看这几处代码究竟被编译成了什么样的IL指令,当然要用到ildasm。
打开附录/Tools目录下的ildasm.exe 。
File-->Open 选择附录根目录下的NHProf文件夹里的HibernatingRhinos.Profiler.Client.Host.dll ,点击打开。如下图所示
依次展开 Rhino.Licensing --> Rhino.Licensing.AbstractLicenseValidator-->IsLicenseValid:bool() 。 如下图所示
我们先不忙着双击打开它, 先View-->选中Show bytes 如下图所示
现在可以双击那个IsLicenseValid方法了。双击后会弹出如下图所示的窗口
这就是IsLicenseValid这个方法反汇编出来的IL代码了。现在我们要在这里找到和我们刚才分析出来的要去掉的那三处源码对应的IL代码。
第一处
throw new LicenseExpiredException("Expiration Date: " + this.ExpirationDate);
对应的是
IL_0085: /* 72 | (70)0021CC */ ldstr "Expiration Date: "
IL_008a: /* 02 | */ ldarg.0
IL_008b: /* 28 | (06)0001AD */ call instance valuetype [mscorlib]System.DateTime Rhino.Licensing.AbstractLicenseValidator::get_ExpirationDate()
IL_0090: /* 8C | (01)000038 */ box [mscorlib]System.DateTime
IL_0095: /* 28 | (0A)000140 */ call string [mscorlib]System.String::Concat(object, object)
IL_009a: /* 73 | (06)000207 */ newobj instance void Rhino.Licensing.LicenseExpiredException::.ctor(string)
IL_009f: /* 7A | */ throw
看到上面的红字标明的部分了吗?这就是刚才show bytes选中后,才会显示的部分, 也就是这段代码的IL指令的十六进制表示了。
我们把他单独提出来
72 700021CC
02
28 060001AD
8C 01000038
28 0A000140
73 06000207
7A
OKAY , 下一步我们要祭出最后一个神器:010Editor了,附录的Tools里也有这个工具,这是个16进制编辑工具,当然你习惯用UltroEditor也可以,只是我比较习惯这个,所以就用这个了。
在打开010Editor之前,先把HibernatingRhinos.Profiler.Client.Host.dll 备份一下,会产生一个HibernatingRhinos.Profiler.Client.Host - 副本.dll
打开010Editor。 File-->Open File 选择 HibernatingRhinos.Profiler.Client.Host - 副本.dll。 点击打开。 打开后如下图
Ctrl + F 或者 Search-->Find (查找-->查找)
如上图,Type 选择 Hex Bytes(h) Value 里输入 72cc210070 (大小写无所谓)
为什么输入72cc210070 ,我们把刚才提出来的IL指令的十六进制表示再拷过来。
72 700021CC
02
28 060001AD
8C 01000038
28 0A000140
73 06000207
7A
看出来什么门道没有,72cc210070 。其实就是第一排的指令,72 这个好理解,但奇怪的是在指令里72后面的是 70-00-21-cc (特意每二个加了一个中间杠作为分隔符)而搜索里写的却是cc-21-00-70 ,正好反过来了。这是为什么呢? 要想明白这个,首先得知道这8个十六进制字符在这里代表的是地址,这就涉及到了地址的big-endian(大端模式)与little-endian(小端模式)的问题了,我们常用的CPU架构基本上都是little-endian小端模式,也就是低位字节放在低地址,也就是象上面看到的那样在内存里看起来是反过来的。 OKAY,输入完这些后,点击Find All,找到一处
再Find All 一次,提示没有了,也就是说只找到一处,这是我们希望看到的,因为只有一处就说明我们直接定位到要改的位置上去了,当然为了保险起见,我们再往后面看一看是不是能和我们后面的指令对应上。看上图70后面是 02 。 我们的下一条指令是02 对应上了,再往下看 28 AD010006,我们的再下一条指令是28 060001AD 又对应上了,剩下的就不具体再看了,这样基本就可以确定了。OKAY,下面开始变得简单了,从72 cc 21开始直到73 07020006 7A全部清0 。 如下图所示
同样的方法,将其它两处对应的IL指令也清 0,其它两处定位就很简单了,就在第一处下面不远的地方,源码虽然是两处,但IL指令其实是连起来的,如下图所示(170BDE08后面红色全0)
OKAY,修改完成后 crtl + s 或者 File--Save 。
关闭ildasm和 010Editor 。 把HibernatingRhinos.Profiler.Client.Host.dll改名为HibernatingRhinos.Profiler.Client.Host_ori.dll 。 然后把HibernatingRhinos.Profiler.Client.Host -副本.dll(刚才修改的那个)改为HibernatingRhinos.Profiler.Client.Host.dll 。 双击Nhprof.exe执行 。 看一看结果
我们可以看到,打开时,已经不提示许可过期了,而且在界面的上部也提示了Connected -89 days remaining。但是 不一会儿问题就出来了,在运行了大概5秒钟后,弹出了如上图所示的 unhandled exception occurered sn Report error to support ? 的错误提示,点是或否之后,虽然不会关闭窗体,但状态会变成DisConnected 然后,File-> load from file , File->save to file , Options-->settings, Options->Connection String 这几个菜单用不起来 。
看来破解的还不彻底。不过有文字提示的话,定位就容易多了,我们可以把这个工程导出去,然后用VS打开,这样就可以利用 VS强大的全工程内搜索进行定位了。
Reflector 导出工程的方法,就不具体介绍了,可以上网搜索 。
又经过一番分析,将解决这个问题的方法定位到了 NHProf.exe 这个Assesmbbly 的HibernatingRhinos.Profiler.Client.Host命名空间下的OutOfProcessClientService类的SelfVerification这个方法里。OutOfProcessClientService这个类我们前面分析过,现在直接看他里面的SelfVerification方法的代码
private static void SelfVerification()
{
Thread.Sleep(TimeSpan.FromSeconds((double) new Random().Next(10, 0x19)));
Assembly assembly = typeof(BuildInfo).Assembly;
byte[] buffer = Convert.FromBase64String("ACQAAASAAACUAAAABgIAAAAkAABSU0ExAAQAAAEAAQDBLTP+od21ZEnX3WXgZVRX1adQQHusYawlMrB4Mnz5vH3GcynLb4CvG7zwjJiYsit/xgN28VmeeQJ5PSdcXH1aB54Qnm4TMa+HcRFxcnGLQQfzwKa/rQIufkQ+Du1hERNZRdERss/wpbnyF2mcNsmGDa09Y+x0264LLK/sK4SIog==");
byte[] publicKey = assembly.GetName().GetPublicKey();
for (int i = 0; i < publicKey.Length; i++)
{
if (publicKey[i] != buffer[i])
{
throw new InvalidOleVariantTypeException("pk");
}
}
bool pfWasVerified = false;
if (!HostProgram.StrongNameSignatureVerificationEx(assembly.Location, true, ref pfWasVerified))
{
throw new InvalidOleVariantTypeException("sn");
}
}
看到红字的那一排了没 , 直接去掉就可以了,这是强名称验证如果没通过, 就直接抛异常。 去掉的方法前面已经讲过了,只不过在这里如果直接搜索这一部分的HEX BYTES会搜到不只一个地方,这就要求参考上下文,再结合这个方法开头那部分的HEX BYTES一起进行搜索,互相参考才能定好位置 , 另外这次的目标不是HibernatingRhinos.Profiler.Client.Host.dll,而是NHProf.exe了 :)
一切操作完成后,重新打开NHProf.exe 。 不再弹出刚才的那个错误了。OKAY,到此已经算是破解成功了。当然有没有什么后遗症或者其它什么隐患,就不去具体测试了。理论上问题不大
这篇到这里就算结束了,但无论怎么讲,开篇毕竟讲了知破才能知防,在结束,总得在这方面总结点什么 。
从上面的分析过程中,我们可以总结出来以下几条。
1. 不要用C#写桌面程序 _^... (我日,能说点人话不)
2. 代码写的越规范,越容易被分析,所以一定要混淆,这在一定程序上可以增大分析的难度。
3. 字符串不要以明文出现,一般的混淆软件都有字符串混淆的功能,如果没有,得自己处理,这样就没办法进行搜索定位了。
4. 验证不要只使用一种方法,就是只使用一种方法,也不要最终集中到一个方法里去实现验证,这样只要找到这一处,就可以直接给干掉了。在你的软件加上个十几二十个验证,尤其是一些意想不到的地方,当他幸幸苦苦去掉了了七八个验证,过了一会儿,发现又需要认证的时候,基本上精神就要崩溃了。
5. 理论上是不存在没办法破解的软件的,只要你有足够的时间和精力再加上 点运气,肯定都可以破解的,然而事实上很多软件是没有破解版的,这就涉及到一个破解成本和破解后所获得的价值的问题了。一个50块钱的软件,需要四五个小时才能破解出来,如果仅仅只是自用的话,恐怕就不会有人去破解他了,而一个值10万块的就不一样了, 如果一个只要50块钱的软件,一个人肯花四五个小时去搞,基本上不是为了自己再去卖,就是纯粹是为了获得满足感(自我满足也是 价值之一)。 所以一个软件的保护,并不是在让所有人都没办法破解,而是让他的破解成本尽可能的大以至于 大到他所获得的价值就可以了
6. 百度或GOOGLE .NET软件保护
下一篇,我们将一起来看一看怎样去写一个破解工具,去实现自动破解 。
附件下载地址:http://www.2cto.com/soft/201304/38186.html