2003
01 02 03 04 05 06 07 08 09 10 11 12
2006
01 02 03 04 05 06 07 08 09 10 11 12
2007
01 02 03 04 05 06 07 08 09 10 11 12
2008
01 02 03 04 05 06 07 08 09 10 11 12
2009
01 02 03 04 05 06 07 08 09 10 11 12
2010
01 02 03 04 05 06 07 08 09 10 11 12
2011
01 02 03 04 05 06 07 08 09 10 11 12
2017
01 02 03 04 05 06 07 08 09 10 11 12
2018
01 02 03 04 05 06 07 08 09 10 11 12
 
Jan
11
2008

Unbreakable Serial Number Scheme

前幾天蕭易玄提及,他寫的共享軟體 Ram Disk Utility ,已經被 Mac 上一個很有名的序號大全(骷髏頭)給蒐羅進去。雖然發生這種事有點遺憾,但我想這也沒那麼糟,畢竟這代表 RDU 已經被國際注意到,而且大家真的會想要去用,也算是一種肯定。

是不是有某些奧客把他買來的序號流傳出去呢?如果是這樣那就還好。你可以比對出這奧客當初購買的基本資料或信用卡等,看是列入黑名單或者和找他算帳。但根據蕭易玄表示,骷髏頭出現的並非是他曾經發過的任何序號,因此我們可以推論,這是那些 cracker 自行產生的序號。在這種狀況下,我們沒辦法找出是誰散佈的序號,因為我們客戶資料庫裡壓根沒有這組序號的主人資料。因此,一個好的序號機制必須防止 cracker 破解出運算規律並且自行產生任意的序號。

難道那些 cracker 有通靈之術,能猜到序號的運算規律?當然並非如此。我花了點時間來看看 RDU 的序號機制。因為這是一個 REALbasic Application,增加了反組譯的難度,不過大概也只花兩個小時,就找出了問題所在。問題在於 RDU 的序號機制和許多軟體一樣,採用的是傳統的檢驗手法:

  1. 首先軟體裡面包含一個自己設計的秘密方法,經過許多複雜的步驟可以把註冊資訊轉換成序號,我們暫且把這個方法叫做 GenerateSN()。
  2. 當使用者輸入註冊資訊時,計算 S = GenerateSN(Name, Oranization, …)。
  3. 如果使用者輸入的序號 UserInputSN 和 S 相同,就視作輸入正確。

看出問題了嗎?就算 GenerateSN() 設計得多複雜、多難懂、用猜的根本不可能猜出來,只要 cracker 提供一些假資料,在第二步的時候 cracker 就可以得到正確的序號 S 了。所以這種檢驗方法的弱點在於「為了檢查序號是否正確,軟體裡面就內建了序號產生器」。

那麼,這樣如何呢?從登入系統得來的靈感(一般登入系統都不會直接比對密碼的明文,而是會比對 hash 過的密碼),如果我們也比對序號的 hash 呢?

  1. 首先軟體裡面包含一個單向(One-Way Function)函數 Hash(),以及一個 GenerateHashedSN(),可以給定註冊資訊(Name, Organization, …)計算出 Hash 過後的序號。
  2. 當使用者輸入註冊資訊時,計算 H = GenerateHashedSN(Name, Oranization, …)。
  3. 如果 Hash(UserInputSN) 和 H 相同,就視作輸入正確。

看起來很不錯。問題是你要怎麼設計 Hash() 和 GenerateHashedSN()?一個最簡單的想法是 GenerateHashedSN(x) = Hash(GenerateSN(x))。但是這樣一來等於是先算出了正確的序號再把它 Hash 過。如此一來還是產生了正確的序號!我們必須規定 GenerateHashedSN() 在計算的過程中不可以計算出真正的序號。因此要設計這套方法就變得很困難了。

其實用抽象化的角度來看,要防止別人產生序號,可以利用數位簽章(請參閱這篇關於公開金鑰系統的文章)的特性:

  • 每個人都可以迅速地為任何他想要的文件產生簽名。
  • 每個人都可以迅速地確定任何一個簽名,是不是某個簽名者對於某個特定文件所產生的。
  • 沒有辦法(在合理時間內)幫其他人為他們沒有簽過的文件產生簽名。

有了這些特性,我們可以如此設計軟體序號的機制:使用者輸入序號時,用 public verification key 檢查 UserInputSN 是不是註冊資料的簽章即可。密碼學上很多這類的演算法可以用,舉個實際用 RSA 簽章的流程實例:

  1. 首先軟體裡面包含一個 public verification key: VK,而成對的 private signing key: SK 妥善地收藏在開發者的極機密電腦裡。
  2. 開發者計算序號的方式是 RSA(Hash(Name, Organization, …), SK)。
  3. 當使用者輸入序號的時候,軟體檢查 RSA(UserInputSN, VK) 和 Hash(Name, Organization, …) 是否相同。(因為 RSA 的特性是加密解密方法一樣,只是 key 不一樣。也就是說 RSA(RSA(m, SK), VK) = m)

這樣就可以保證別人很難偽造序號了。

已經有別人做好這些事情提供 framework 了,叫做 Aquatic Prime

不過注意這篇文章標題並不是「無敵的軟體保護機制」,這篇文章講到的方法只有做到「很難讓人偽造序號」。序號本身無法破解並不是代表這軟體就無法破解了,破解的方法很多,把你軟體裡面的 Verification Key 抽換掉、直接更改 binary 機械碼、甚至像 Aquatic Prime 這種 open source 的 framework 更簡單,你自己編譯一個改過的版本替換掉就好。不過「無法偽造序號」代表了一個基本的意義:那些盜版散播的難度會變高。例如你要改 binary ,那麼軟體每一次更新的時候你都得重新改一次,很麻煩。而且現在盜版大家也越來越有安全觀念,既然別人可以修改註冊機制,那為什麼不能放進木馬之類的東西?網路上來路不明的破解檔最好不要用,去軟體網站抓正版然後輸入盜版序號才是正確(?)的方法啊!不能偽造序號,也就等於提高盜版者的風險。

(這篇文章考慮了很久才寫出來,裡面已經盡量避免對破解的部份說明太清楚。但是軟體保護這種東西本來就得知道攻擊手法才能了解原理。當然是希望大家都用正版,如此一來軟體也不用保護了……)

 

Annotations RSS

“可以列入 “骷髏頭” 也算是有名了…

只要想想連大公司的軟體也到處走了
小軟體又如何呢 ?
我有Email 給你…有空看一下吧….”

---Brent. 1/12, 2008

“收到~”

---yllan. 1/16, 2008
 
 

Write Concisely