防止软件被暴力破解
本文描述建立一個什么樣的初步防護框架可以對抗破解者的暴力破解的思路,文章不涉及軟件正常注冊后的防護問題。
一、原理
十幾年來隨著軟件業的發展,對于軟件產品的防范問題一直是保護開發利益的主要考慮問題,一些相應從事軟件防護的軟件公司應運而生,產品的種類亦是品種繁多, 這些基于軟件和硬件的防護產品,其防護水平更是強弱有別,但對于抵抗暴力破解還都存在著一定的問題。為此本文提出了一個將序列號與程序關鍵代碼結合的防護方案設想,在未知正確注冊碼的情況下,以目前的數學技術是不能破解的。
其實現原理是使序列號與程序的某些關鍵代碼和數據發生關系,比如用序列號或其散列值對程序的關鍵代碼或數據進行解密(當然這些關鍵代碼或數據事先是在軟件作者那里進行加密后才發行的)。這樣,即使解密者通過修改判斷跳轉指令可以得到一個看似注冊的版本,但是不正確的注冊碼只會使得解密出來的那段代碼或數據全是垃圾,根本無法使用。具體說來可采用如下的方法。
在軟件程序中有一段加密過的密文C,這個C既可以是注冊版本中的一段關鍵代碼,也可以是使用注冊版程序的某個功能所必需的數據。
當用戶輸入用戶名和序列號之后計算解密用的密鑰:密鑰 = F(用戶名,序列號)。
對密文進行解密:明文M= Decrypt(密文C,密鑰)。
利用某種散列算法計算解出來的明文C的校驗值:校驗值 = Hash(明文M)。散列算法可以采用MD5、SHA等,也可以采用CRC32等。
檢查校驗值是否正確。如果校驗值不正確,說明序列號不正確,就拒絕執行。
?
二、準備工作
先選擇一段代碼作為要加密的數據,你可以選擇軟件某個功能的核心代碼,此例選擇“關于菜單”這段代碼。為了編程時能處理這段代碼,用begindecrypt和enddecrypt兩個標簽將關鍵數據括住,編譯后的數據稱之為明文M。程序編譯好后必須對明文M加密處理,得的數據就是密文C,這樣才能發行。
| // 待加密的代碼 case IDM_HELP_ABOUT : begindecrypt: DialogBox (hInst, MAKEINTRESOURCE (IDD_ABOUT), hDlg, AboutDlgProc) ; enddecrypt: break; |
?
RegCode( )函數的作用是先取用戶輸入用戶名和序列號之后,用戶可以自定義某種算法計算解密用的密鑰:密鑰k = F(用戶名,序列號)
然后對密文C進行解密:
明文D= Decrypt(密文C,密鑰)
利用散列算法(如MD5)計算解密恢復的明文D的散例值,如散例值與加密前的明文M散例值相同,則注冊成功,否則失敗。這樣正確的注冊碼會使得解密出來的那段代碼可以執行,如注冊碼不正確,解密出來的數據全是垃圾,根本無法使用。
實際操作中,也可不利用散列算法計算明文D的校驗值,而利用SEH來處理加密代碼,如解密出的是亂碼,則會觸發異常,這樣就可利用SEH告知用戶注冊失敗提示消息。
源碼如下:
| // 序列號核心判斷代碼 BOOL RegCode ( HWND hWnd) { _asm mov address1,offset begindecrypt // 取待加密代碼首地址 _asm mov address2,offset enddecrypt // 取待加密代碼末地址 unsigned char M-hash [MAXINPUTLEN]={填入經計算過明文M的散列值}; ? 密鑰k = F(用戶名,序列號) // 讀者自行設計一個函數 Decrypt (address1,address2,k); // 調用解密處理函數 szHash= 明文D的散例值 // 計算address1到address2這段解密后代碼的散列值 if(lstrcmp(szHash,M-hash)) // 比較解密后的散列值是否與明文M的相等 return TRUE; else return FALSE; } // 解密函數,將密文C數據與密鑰k進行異或運算 void Decrypt (DWORD address1,DWORD address2,BYTE value) { _asm { pushad mov ecx,address2 sub ecx,address1 // ECX =需要處理數據的字節數 mov esi,address1 mov al,value decrypt: xor byte ptr[esi],al // 解密運算 inc esi loop decrypt popad } } |
?
三、加密算法選用
解密算法應該是整個設計的關鍵。確保算法應該無法讓人逆推,因為程序代碼并不是真正的隨機數據,因此存在著攻擊的可能性。所以Decrypt算法使用不對稱算法很重要,否則別人可以假設明文,反推密鑰進行攻擊。
此例便于講解方便,選用了最簡單的XOR來加密數據,由于程序代碼不是隨機數據,攻擊此類加密只是幾分鐘的事。因此實際操作時,加密數據切勿以字節來加密,并且不要用XOR來加密。
?
四、手動加密代碼
為了能在十六進制工具中方便找到這段“關于菜單”代碼,待加密的源代碼用下面兩行匯編代碼括住:
_asm inc eax 機器碼是x040
_asm dec eax 機器碼是x048
這樣上面的代碼變成了:
| // 待加密的代碼 case IDM_HELP_ABOUT : _asm inc eax // 在十六進制工具中對應0x40 _asm dec eax // 在十六進制工具中對應0x48 begindecrypt: DialogBox (hInst, MAKEINTRESOURCE (IDD_ABOUT), hDlg, AboutDlgProc) ; enddecrypt: _asm inc eax // 在十六進制工具中對應0x40 _asm dec eax // 在十六進制工具中對應0x48 break; |
文件編譯好后,用十六進制工具打開文件,搜索16進制數據“4048”,就能找到待加密的代碼明文M。如下圖黑影部分所示:
?
先用Hash函數計算器等工具計算這段數據(明文M)的散列值:
校驗值M-hash = Hash(明文M)
將這個結果重新填入源碼中,再編譯。
然后將密鑰k自定義一個值,加密這段代碼(明文M):
k XOR M =密文C
讀者可以自己寫一個工具來完成異或加密或用解密工具Hiew來完成此類工作。
?
五、使.text區塊(section)可寫
在Win32平臺上(包括Windows 95/98/ME/NT/2000/XP/CE),可執行文件是PE(PortableExecutable)格式。PE文件使用的是一個平面地址空間,所有代碼和數據都被合并在一起,組成一個很大的結構。文件的內容被分割為不同的區塊(Section,又稱區段、節等),塊中包含代碼或數據,各個塊按頁邊界來對齊,塊沒有大小限制,是一個連續結構。每個塊都有它自己在內存中的一套屬性,如:這個塊是否包含代碼、是否只讀或可讀/寫等。
文件編譯后,.text區的屬性是只讀的。但是,我們的程序會向.text區塊寫入新的數據(xor byte ptr[esi],al指令 ),由于.text區塊只讀,這樣會導致程序崩饋。
因此必須用PE工具,如LordPE或Prodump等改變.text區塊的屬性(characterics)為 0xE0000020 ,表示可讀,可寫,可執行。如下圖所示:
?
還有一種方法不修改區塊屬性,而是編程時用“VirtualQuery”、“VirtualProtect”等函數修改內存的讀寫屬性,這樣就可直接向.text等區塊寫數據了。
?
六、重定位
文件執行時將被映像到指定內存地址中,這個初始內存地址稱為基地址(ImageBase)。對于EXE的程序文件來說,Windows系統會盡量滿足。不過對于DLL的動態鏈接庫文件來說,Windows系統沒有辦法保證每一次DLL運行時提供相同的基地址,這樣對于DLL文件,“重定位”就很重要了。
由于此例為EXE,故沒有提到重定位問題的解決。對于DLL文件,把代碼做為加密對象而言,如果代碼中有重定位數據,則加密后的密文解密后還需要對其進行重定位,否則這段代碼就算是正確解密也無法運行。希望讀者注意這點。
轉自---------------看雪論壇轉載于:https://www.cnblogs.com/Sendige/p/9601019.html
總結
- 上一篇: 常见异常代码oracle
- 下一篇: linux 参数