作者:DBinary
來源:https://www.zhihu.com/question/356008082/answer/1128751775
理論上不存在無法破解的軟件,但并不是說破解軟件比開發(fā)容易一萬倍,naive,防破解的目的不是要做一個無法破解的軟件,而是:
讓破解軟件的成本遠(yuǎn)大于購買軟件的成本,知名軟件大多都有實力讓軟件破解難度大大加大,但出于市場需求和爭取用戶的角度考慮,會放水!
光說不練假把式,52老司機來給各位簡略表演幾個有代表性的軟件破解,當(dāng)然,要看懂下面的內(nèi)容,需要一點點C語言的編程基礎(chǔ)。
我們先來一個最最簡單的軟件破解,假設(shè)我們寫了一個軟件,它的注冊手段代碼如下:
#include "stdio.h"
#include "string.h"
int main()
{
char Key[32];
printf("請輸入注冊碼:");
gets(Key);
if (strcmp(Key,"abc123456")==0)
printf("注冊成功");
else
printf("注冊失敗");
}
超簡單的,你把這個程序后綴改成txt然后打開,搜索注冊碼,然后翻一翻key就找到了。
那么,讓我們進入防破解V2.0時代,為了與時俱進,我們稍微把上面的代碼改一下:
#include "stdio.h"
#include "string.h"
#include "windows.h"
#include "math.h"
int main()
{
char iKey[32];
char Key[32];
char ID[32];
int iID=0xabc1d3f;
sprintf(Key,"%x",iID*8+123456);
printf("你的機器碼是%x\n",iID);
printf("請輸入注冊碼:");
gets(iKey);
if (strcmp(Key,iKey)==0)
MessageBoxA(0,"注冊成功","",MB_OK);
else
MessageBoxA(0,"注冊失敗","",MB_OK);
}
現(xiàn)在"TXT"破解法已經(jīng)不頂用了,你看,key找不到了。
怎么樣,這種保護手段是不是熟悉的味道熟悉的配方,這個機器碼可以從網(wǎng)卡MAC,CPU型號,內(nèi)存大小等等等等去生成。
當(dāng)然,key的算法也可以拉上MD5,SHA等等等等手段來弄,而不是簡簡單單的乘一個8再加上123456,總之從機器碼到注冊碼的算法你能玩出花。
打開ollydbg,對MessageBoxA下斷點(也就是彈窗的函數(shù))。
然后輸入一個錯誤的注冊碼,命中斷點,很快,我們來到了判斷注冊碼是否正確的邏輯處理代碼。
你猜猜,要是我們把這個判斷注冊碼是否正確的代碼給它刪了)(nop指令填充)會怎么樣?
你會發(fā)現(xiàn),握草,不管我輸入什么,都是注冊成功!
你發(fā)現(xiàn),萬惡之源都始于那個MessageBox函數(shù),正是這個函數(shù),讓我們順藤摸瓜找到了注冊碼判斷代碼。
關(guān)注微信公眾號「程序員的成長之路」后臺回復(fù)「2048」關(guān)鍵字,免費獲取5T技術(shù)學(xué)習(xí)資源!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。
時至今日,仍然有非常非常大的一部分軟件使用著這個二三十年前就在用的保護手段,每年死于MessageBox被破解的軟件,圍起來可以繞地球三圈,也就是這個保護機制,成就了所謂軟件發(fā)布后十分鐘就被破解的悲慘結(jié)局。
這個時候,你痛定思痛,mmp,有內(nèi)鬼,既然MessageBox不好用,那我不用就是了,于是你把代碼改成下面這樣:
#include "stdio.h"
#include "string.h"
#include "windows.h"
#include "math.h"
int main()
{
char iKey[32];
char Key[32];
char ID[32];
int iID=0xabc1d3f;
sprintf(Key,"%x",iID*8+123456);
printf("你的機器碼是%x\n",iID);
printf("請輸入注冊碼:");
gets(iKey);
if (strcmp(Key,iKey)==0)
printf("注冊成功");
else
exit(0);
}
你看,嘖,只要你注冊碼沒輸對,我直接把軟件退出(或者跳到別的地方),看你怎么辦?
可惜,內(nèi)鬼年年有,"注冊成功"幾個字還是出賣了你,打開ollydbg,查找字符串參考,然后雙擊。
你察覺到這樣一個地方判斷注冊碼實在不靠譜,所以,你改變了策略,把檢查注冊碼的代碼復(fù)制了n遍,或者逐字符檢查注冊碼的準(zhǔn)確性,還有人將注冊成功等字樣進行加密或混淆,等到要用的時候再取出來。
#include "stdio.h"
#include "string.h"
#include "windows.h"
#include "math.h"
int main()
{
char iKey[];
char Key[32];
char ID[32];
int iID=0xabc1d3f;
sprintf(Key,"%x",iID*8+123456);
printf("你的機器碼是%x\n",iID);
printf("請輸入注冊碼:");
gets(iKey);
if (strcmp(Key,iKey)==0)
printf("注冊成功");
else
exit(0);
if (strcmp(Key,iKey)==0)
printf("注冊成功");
else
exit(0);
if (strcmp(Key,iKey)==0)
printf("注冊成功");
else
exit(0);
if (strcmp(Key,iKey)==0)
printf("注冊成功");
else
exit(0);
if (strcmp(Key,iKey)==0)
printf("注冊成功");
else
exit(0);
if (strcmp(Key,iKey)==0)
printf("注冊成功");
else
exit(0);
if (strcmp(Key,iKey)==0)
printf("注冊成功");
else
exit(0);
if (strcmp(Key,iKey)==0)
printf("注冊成功");
else
exit(0);
}
你放心,不管你復(fù)制多少次,要找出來都是時間問題,吃棗是會被破解的.同時只要你要用到明文字符串你遲早還是要解密的,這種手段類似于加upx壓縮殼,只需要等待數(shù)據(jù)解壓完成,所有的東西又都是明文的了。
因此比較聰明的做法是,要用時解密,用完后馬上把明文抹掉,這樣說不定能拖延更長的時間。
你開始發(fā)現(xiàn),與其揪出內(nèi)鬼,不如主動出擊,也就是我們著名的與其解決問題不如解決提出問題的人,終于,你開始對調(diào)試器下手了,然后你成功進化到防破解V2.5時代。
這個時候,不得不介紹一個老掉牙的但非常有名的函數(shù):
簡單來說,當(dāng)我們破解一個程序的時候,大部分情況下我們會打開一個叫調(diào)試器的東西來對軟件進行反編譯分析,誒,重點就在這。
IsDebugPresent這個函數(shù),就能檢測我們的程序有沒有被一個調(diào)試器附加,你想啊,正常情況我們用軟件誰會吃飽撐著附加一個調(diào)試器來用,你要是用調(diào)試器附加我,你肯定就是想干壞事。
#include "stdio.h"
#include "string.h"
#include "windows.h"
#include "math.h"
int main()
{
char iKey[32];
char Key[32];
char ID[32];
int iID=0xabc1d3f;
if (IsDebuggerPresent())
{
MessageBoxA(NULL,"小樣,就你還破解我的程序,回家喝奶去吧","",MB_OK);
return 0;
}
sprintf(Key,"%x",iID*8+123456);
printf("你的機器碼是%x\n",iID);
printf("請輸入注冊碼:");
gets(iKey);
if (strcmp(Key,iKey)==0)
printf("注冊成功");
else
exit(0);
}
于是,當(dāng)下次再ollydbg加載調(diào)試你的程序的時候,就會出現(xiàn)下面的情景:
曾經(jīng)很長一段時間(包括現(xiàn)在),很多軟件或加密殼都會檢查是否有調(diào)試器正在調(diào)試自己,比如tls段會在加載時被執(zhí)行,如果檢查到自己正在被人調(diào)試破解,就會設(shè)置一個tag讓程序跑到?jīng)]啥用的地方去或者直接退出重新,也有利用變形的PE頭讓調(diào)試器無法加載,總之手段很多。
可惜這還是沒什么卵用,比如IsDebugPresent可以通過修改FS寄存器的標(biāo)志位來讓它徹底啞火,同樣的手段包括但不限于檢查int 3軟中斷,Raw Call,Query PEB,檢查Debug Privilege和父進程等等等等,都有繞過的方式.
好了,還有啥法子不,放心道高一尺魔高一丈,現(xiàn)在我們來到了現(xiàn)在最流行的,防破解V3.0 VMP時代。
先澄清一下,這里的VMP并不是VMP殼,它全程叫Virtual Machine Protection,簡單來說,為啥我們破解軟件那么輕車熟路,還不是因為x86 x64 arm的那堆匯編指令集我們太熟悉了唄。
要是我們自己發(fā)明一套指令集,然后用這個指令集寫程序并運行在我們自己的虛擬機上,那么,破解者一進來,看到的不就是一臉懵逼了么?
可惜的是,VM的運行機制決定了它可能造成幾十倍乃至幾百倍的性能損失,所以,VMP必須用于保護那種關(guān)鍵且不是性能瓶頸的代碼,否者你的軟件跑起來就會像:
當(dāng)然不是,VMP說白了,也只能做到延長分析時間,你要是把VMP做的足夠復(fù)雜,足夠讓一個Cracker醉生夢死了,但是如果這個時間足夠久,你的軟件足夠的值錢讓人有欲望來破解,他們?nèi)匀豢梢猿浞值胤治瞿鉜M機的運行機制,當(dāng)你的VM機運行機制被摸清了,軟件就離淪陷不遠(yuǎn)了。
不過你可以放心,分析VM機執(zhí)行機理,可比自己寫VM機要頭疼多了,畢竟一個是你需要通過代碼來揣測別人的思路,而另一個本身是自己的思路轉(zhuǎn)為代碼,因此基于這點可以說:
你可能會問了,為什么現(xiàn)在市面上那么多軟件,那么多游戲,購買了那么多聽起來那么牛逼的保護軟件,結(jié)果還是被破解了。而且剛發(fā)出來一天就被破解了。
其實很大的問題就出在這個商業(yè)保護軟件(比如保護殼)上,因為這類保護殼大多都會被賣給一大票的軟件開發(fā)商,有一句話叫樹大招風(fēng)。
就像現(xiàn)在流行的VMP保護機制,之所以能保護,是因為其運行機理破解者不明確,如果你這個軟件就給自己用,而且你這軟件還不怎么值錢,除非大佬空虛寂寞冷,不然誰會有那閑工夫去分析你的虛擬機是怎么跑的。
但商業(yè)保護殼不同,不論其采用什么樣的保護機制,只要分析過一遍搞清楚了,幾乎所有使用這類保護機制的軟件都會淪陷,而且在灰色產(chǎn)業(yè)上.這種破解甚至還頗有利可圖,只要這個保護機制不更新,一次投入,長期回報。
于是,只有說在第一次分析時會花上很長的時間,之后就都只是玩套路了。
因此,購買商業(yè)保護殼,其實其保護效果并沒有想象中的那么強,很可能在灰色產(chǎn)業(yè)中形同虛設(shè),甚至一個具有反逆向基礎(chǔ)的碼農(nóng)自己寫的說不定還更有效果。
當(dāng)然一個軟件是被破解概率高不高,仍然是我之前提到的那句話:防破解不是讓軟件無法破解,而是讓破解軟件的成本遠(yuǎn)大于購買軟件的成本。
畢竟你說你一個軟件拿來開源都沒人愛用,你還整天琢磨著怎么才不會被破解,寒摻不老鐵。
那么你會開始問了,有沒有更給力點的防破解技術(shù)?
好像之前說的說來說去,無非就是拖延時間,誒,這個我們要擺正心態(tài),不論是加密還是破解,其實說白了最終就是拖延時間,你看那些加密算法,依據(jù)其數(shù)學(xué)理論,如果要破解,它的計算量就算你把全世界的計算機加起來一塊算,也夠你算三個世界末日了。
不過別擔(dān)心,更給力的方法還是有的,你想啊,為什么我們之前說了那么多軟件都被破解了,最最關(guān)鍵的一個原因,是我們能搞到代碼,即使這個代碼已經(jīng)是經(jīng)過編譯后的一堆匯編指令,但只要我們有這堆代碼,遲早我們還是能搞懂這個程序是怎么回事的,然后我們就可以對癥下藥干壞事。
這就像給你有一包面粉,而程序就是一個面包機,你把面粉塞進面包機做出了面包,有天你好奇啊,這面包機咋整咋整就出來一塊面包了呢,你就動手把面包機拆了,然后你就知道面包機是怎么回事了。
所以有沒有辦法不讓用戶知道我們的代碼是什么樣的呢,就像你把面粉交給了面包師傅做面包,這個面包是怎么做的,你就只能指望看面包師傅有沒有這個心情告訴你了。
為此,有請早期一個相當(dāng)流行且普遍的游戲防破解工具(物理)。
你現(xiàn)在可能表情是一臉問號,但我沒和你開玩笑,在2000年時代,大部分的游戲運行在光盤CD中,但盜版也容易啊,把光盤里的數(shù)據(jù)一復(fù)制下來,然后就可以復(fù)制出一萬張盜版光碟,所以游戲廠商們就想辦法,想來想去就想到了榔頭。
其操作方法很簡單,拿榔頭和釘子,在光盤上釘幾條刮痕出來,造成人工的壞道,然后再將數(shù)據(jù)燒錄到正確的扇區(qū)中,這樣下來雖然程序還是可以正確運行,但是當(dāng)光頭讀盤讀到這個壞道的時候,就會讀不過去。
于是你會發(fā)現(xiàn)打開光盤后,沒有關(guān)鍵的文件,這樣你就沒辦法將游戲或者說程序拷貝出來了,同時還會對壞道的位置做一個標(biāo)記,游戲運行時也會檢查這個標(biāo)記,那么想要盜版你就也得拿起榔頭在光盤同樣的位置上砸出同樣的刮痕出來,當(dāng)然,這幾乎是不可能的。
當(dāng)年這個技術(shù)當(dāng)年又叫防盜環(huán)技術(shù),但不管它的名字叫的多高大上,本質(zhì)上就和榔頭釘釘子如出一轍是同樣的東西,可惜虛擬光驅(qū)出現(xiàn)后,同樣有辦法復(fù)刻光盤的一切數(shù)據(jù)(包括壞道),所以,這個技術(shù)拿到今天來看并沒有什么卵用.不過這仍然給了我們足夠的啟發(fā)。
現(xiàn)在讓我們進入防破解V3.x時代,之所以不叫V4.0是因為這類技術(shù)很早就有并且比VMP保護流行的時間還早的多,而且它可能是最近接理論上不可破解的防破解手段。
我們先聊的是加密狗或者又叫Ukey保護,就是運行軟件你需要插入一個U盤一樣的東西到電腦上,實際上這個Ukey是一個微型電腦,軟件的一些關(guān)鍵的算法和代碼,都在這個UKey的芯片里。
當(dāng)我們PC上的軟件運行后,當(dāng)我們需要執(zhí)行這類關(guān)鍵算法時,我們會向這個Ukey傳遞數(shù)據(jù),然后UKey將結(jié)果計算出來,返回給PC的軟件上,這樣就避免了用戶直接能夠逆向取得關(guān)鍵的算法代碼,破解也就無從談起了。
這也就是為什么到了今天,Ukey保護仍然非常的流行。
可惜,UKey保護仍然有諸多的限制,首先就是帶著一個Ukey賊麻煩,萬一UKey丟了補辦是一個麻煩事,運行軟件插Ukey也是個神煩的事情。
同時,UKey的性能決定了它可能不能執(zhí)行一些過于消耗性能和內(nèi)存空間的代碼,數(shù)據(jù)交互也因帶寬和通訊延遲會造成性能損失,所以它和VMP保護機制一樣,同樣不是一個省油的燈。
同時開發(fā)人員的水平不到位,該保護的代碼沒保護,保護來沒啥用的代碼塞了一堆,也會給Cracker帶來機會,而且只要你的軟件夠值錢,你是不是太瞧不起我華強北了。
把Ukey拆開來,使用某種"藥水"剝開外層找到內(nèi)部的芯片并接上已經(jīng)熔斷的"讀引腳"(有些芯片連這步都省了,直接熱風(fēng)槍一吹接板讀ROM) 然后再把芯片的代碼給讀出來。
你發(fā)現(xiàn),只要是把實體的東西交到用戶的手上,遲早會出問題,所以,這個Ukey保護現(xiàn)在大部分情況下變成了帶數(shù)字證書的網(wǎng)絡(luò)驗證模式,這類的關(guān)鍵代碼從Ukey轉(zhuǎn)移到了服務(wù)器上,數(shù)據(jù)交互通過網(wǎng)絡(luò)來做。
其實這種保護機制和Ukey保護原理是一樣的并沒有什么本質(zhì)的區(qū)別,但同樣處于網(wǎng)絡(luò)帶寬也延遲的考慮,同樣具有一定的性能損失和設(shè)計缺陷。
需要重點提及的是,這類網(wǎng)絡(luò)保護的手段必須專門設(shè)計以保護程序中的一系列關(guān)鍵"功能"代碼而不是"防破解"代碼(比如代碼解密,注冊驗證),因為后者幾乎沒啥卵用仍然能夠?qū)?防破解"的代碼清除或Dump解密代碼或偽造本地服務(wù)器實現(xiàn)破解。
所以你指望一堆什么x盾,x寶一鍵能一勞永逸一鍵保護程序,程序必須經(jīng)過專業(yè)碼農(nóng)而不是彩筆專門的設(shè)計才能起到其應(yīng)有的保護效果。
但現(xiàn)實情況是,處于用戶離線運行和性能延遲瓶頸的考慮,這種網(wǎng)絡(luò)保護設(shè)計的往往都有很大的缺陷,因此,并不是說這東西不好,而是理想很豐滿現(xiàn)實很骨干,實在無能為力啊。
當(dāng)然,代碼保護的手段很多且花樣玩法也很多,本文只是初略介紹幾個比較有代表性的破解和反破解手段。如有興趣。
-END-
免責(zé)聲明:整理文章為傳播相關(guān)技術(shù),版權(quán)歸原作者所有,如有侵權(quán),請聯(lián)系刪除