我日,好讓人蛋疼的題目 。
當然這個題目的確是稍稍使用了一些誇張的修辭手法。但是只要思路對頭,再加上點運氣,破解也並不是如你想象中的那般神秘。
為何突然想寫這方面的東西呢,那是因為三年前,我曾在CSDN的資源上發布了一個自己寫的NHPorfilerCrack(http://download.csdn.net/detail/jivi/2231869 ),當時寫這個工具是為了破解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.<StartWithOutOfProcessListener>b__a); 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軟件保護
下一篇,我們將一起來看一看怎樣去寫一個破解工具,去實現自動破解 。
博客園上傳空間有限和單個文件限制,所以放到了CSDN上,沒有CSDN帳號的,下載的話可能需要注冊一個。博客園上傳空間實在小的太蛋疼了