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