遊戲開發日誌 #07
看到題圖,就知道這次想說的是加密。
今次會比較深入地去說明一下加密到底在做什麼。
也會把本人最新的加密方法作較詳細的說明。
關於遊戲資料為何需要加密,可以先看<遊戲開發日誌 #04> 。
首先,什麼是加密?
加密最早出現在一些知識相關的文獻或書籍上,為了只把知識傳給認可的學徒,把獨特的文字取締,只有同門才可以解讀。
最有名的就是西洋的鍊金術和中國的鍊丹術。
鍊金術和鍊丹術都是現代化學的起源,西洋發展成科學,中國發展成醫學。雙方有很多共通點,例如尋求長生不老、改變質量等;黃金是共同的不滅象徵,金丹、金人等字也是代表了不老不死的仙丹和不老不死的人。
加密的基本,就是把本來的文字變成其他文字,但只要有足夠的樣本和文章的長度,還是可以解讀。古時懂得讀寫的人很少,所以加密的程度也不高,但當讀寫已不是高人貴俯的專利時,更複雜的加密法變得更重要。
只讀文章的第一個字母或生字、以每行文字數量轉換成代碼等等的方法開始出現,特別以必須要參考多個不地方才可以解讀的方法被使用得最多,例如特務電影中,看當天幾份報紙的某個專欄,再以當中的某些文字來得到完整訊息也是很常見。
而人類的歷史和文化中佔了最大比重的「戰爭」,對於情報的管理更是著重,因為只要被敵對國得知行軍情報,就等於被宣告戰敗沒分別。
在人類的特質中不可或缺的「七大罪」告訴我們,即使沒有人被收賣,也會有臥底去出賣情報,而解決方法很簡單,就是離開司令部的情報都是已加密的,只有接收方的司令部才有解密的方法,除非司令部中有間諜或臥底,否則情報流失也沒有任何問題了。
唯一要解決的是加密和解密的方法,由人去做的話就只會出現上述的問題,所以一部專門加密和解密的機器 - Enigma Machine 就此誕生了。
如果有看過 2014 年的電影 The Imitation Game 的話,應該會知道上面這部機器。
Enigma Machine,Enigma 是迷的意思,這是在二戰時大量被使用在軍報傳送上的加密和解密機器。Enigma Machine 有很多不同型號,但運作都沒有多大差異,只有複雜程度的分別。
這部加密解密機最強大的地方,就是以五次轉換來作字母的加密,每次鍵入字母後加密法都會改變,而加密法還可以自己更換和設定。
這樣說還不會太明白,所以做了上面這張圖作例子:
- 鍵入 Q
- Q 經過接線連到 R
- R 經過旋轉機 Roller 第一格連到 D
- D 經過旋轉機 Roller 第二格連到 G
- G 經過旋轉機 Roller 第三格連到 O
- O 回到接線連到 M
- 最後得出 M
- 旋轉機會像計數器一樣自動跳動一格
- 再鍵入 Q 的話已不會得出 M
接線可以自行更改,旋轉機可以更換,也可以先轉到一個組合。
只是這樣看的話好像不是太複雜,但這個加密機的加密可能性,已經不是沒有電腦的二戰時代的人類可以有能力解開了,因為其加密的可能性實在太多了:
- 一個字母經過 5 重 26 個可能性
- 26×26×26×26×26 = 11,881,376 個可能性
- 如果加密一個 APPLE 生字,由於每次鍵入一個字母,旋轉機都會轉動一次,我數學太差已經不懂如何計算了,一定已經是數百億個可能性了。
就像電影中所說,德軍每一天都會把接線更改,也會把旋轉機的組合更改,所以對於英法等國家來說破解時限就只有一天,因為第二天的加密情報就已經不同,樣版不同的話之前一天的努力也是白廢。
解決法也像電影中所說,必須滿足兩個條件:
- 取得加密機/加密機的設計圖
- 取得每天加密的設定組合
到了現代,因為有電腦,在日常中已經使用了很多單向加密法,也就是不能解密的加密法,例如 SSL 就是最被廣泛使用的,包括信用咭。
但遊戲運作的資料不同,因為跟行軍情報一樣,需要解密來使用,所以就要使用可以解密的加密法了。
只要可以解密,就可以被破解。提到破解,大部份人都會認為問題是所需時間,但這只是單向加密法的情況,在雙向加密法上所需要的是足夠的樣本,有足夠的樣本才可以逆算出加密用的設定組合。
比較幸運的是,如果使用 Enigma Machine 的百份一加密組合也好,在遊戲中要取得足夠的樣本,完全破關 100 次也可以了。
在遊戲中最重要的資料,基本就是數字,數字就只有十個,所以加密的組合也不算多,如使用 Enigma Machine 的方法運作,就會有加密組合太小的問題,也就是逆算所需的樣本減少。
簡單說,加密法必須條件跟破解法必須條件是一樣的:
- 加密機制的設定
- 加密組的複雜性
在這裡,本人的加密法就以多重轉換機制以及複雜的加密組,把遊戲中的重要數值、儲存資料作加密和解密處理。
本人的雙向加密法,也是多重轉換,最重要的兩部份就是轉換 UTF8 Bytes[] 和 Encrypt Group 加密組,就等於 Enigma Machine 中的接線和旋轉機 Roller 一樣,到最後所有型態的資料都會加密成 String[]。
跟 Enigma Machine 一樣,每次加密同一樣的文字也會得出不同的加密結果,不同的是並非每次加密都會轉換加密組,而是每次從加密組中隨機取出加密碼。
在這裡先看看相關的程序吧。
<參數 Parameters>
// Settings
private enum EncryptGroup
{
//0 1 2 3 4 5 6 7 8 9
khj, PSw, Avy, kCZ, sxX, y2D, xWg, nDk, YSm, hQR, // 0
rzk, z4O, k8P, bG9, jAj, FGx, OCS, TS6, yYR, IKo, // 1
VZs, QnM, JkF, X4Q, oXR, vnm, WGi, k4z, Bh4, Wqf, // 2
acV, VQD, M8D, UWe, IdP, ghJ, wof, UGw, Xuk, rBN, // 3
DGU, GAh, ihz, ELq, XWJ, k5g, EuY, YeM, RFk, I5G, // 4
XQ2, nQp, NFB, QwS, MdP, RCd, K1k, n5U, cFn, V7H, // 5
wmV, HdN, zIB, Rh5, bZ0, qps, zVe, VrL, Vhy, caf, // 6
Lme, j2W, PSJ, bNk, S2d, Jqu, fwK, qM7, DDx, TcH, // 7
GKC, lhi, Ldu, nY3, mFn, lAa, UR9, prZ, mRW, W6H, // 8
hVG, JEa, xhe, yNX, URf, Hg7, c4o, WqD, nre, INU, // 9
oDg, nxU, yxp, v2Z, G7x, kYB, IKf, VGR, h0e, J3X, // -
QvN, XO2, fXY, sk5, aU1, NQL, SAu, rRr, Vyt, WEZ, // .
RsT, pJk, xP6, F9n, f3j, M1E, WvP, TYg, OkN, E3G, // ""
h3O, x0b, Zvz, TMU, VJm, FMq, aL9, YAa, Pu3, qKG // null
}
private static readonly int // Change follow int for your way
encStrLength = 3, // Length of each encrypt string
encStrGroup = 10, // how many group of each character (column)
intMultiply = 2,
intPlus = 1;
// Don't modify if you don't know what it is.
private static readonly EncryptGroup
encGroup = new EncryptGroup();
private static readonly string
encryptRef = "0123456789-.";
最重要的加密組 EncryptGroup,使用 enum 是因為當中不會出現重複。然後就是一些其他運作用的參數,例如整數再計算的 intMultiply 和 intPlus。
加密組的運作:
- 每一行加密組都代表一個數字
- 當加密一個數字時,會在該行隨機取一個加密碼
- 所以每次加密同一個數字都會有不同的結果
- 如要降低重複性,可以加大每一行的加密碼數量
加密組的設定:
- encStrLength 每個加密碼的長度
- encStrGroup 每行加密組中的加密碼數量
- 可以使用 Encrypt Group Generator 自行生成加密組
<加密 Encrypt>
// Functions
private static int EncStrLine(string value)
{
for (int n = 0; n < encryptRef.Length; n++)
{
if (value == Convert.ToString(encryptRef[n]))
return n;
}
return 999;
}
// Encrypt Methods
public static string EncryptInt(this int value)
{
value = value * intMultiply + intPlus;
string txt = value.ToString();
int len = txt.Length;
string newTxt = "";
for (int i = 0; i < len; i++)
{
int r = UnityEngine.Random.Range(0, encStrGroup) +
encStrGroup * EncStrLine(Convert.ToString(txt[i]));
newTxt = newTxt + ((EncryptGroup)r).ToString();
}
return newTxt;
}
public static string[] EncryptString(this string value)
{
int len = 0;
byte[] utf8Bytes;
string[] newTxt;
if (value == null || value == "")
{
newTxt = new string[1];
len = value == null ? 1 : 0;
int r = UnityEngine.Random.Range(0, encStrGroup) +
encStrGroup * (encryptRef.Length + len);
newTxt[0] = "" + (EncryptGroup)r;
}
else
{
utf8Bytes = System.Text.Encoding.UTF8.GetBytes(value);
newTxt = new string[utf8Bytes.Length];
for (int i = 0; i < utf8Bytes.Length; i++)
{
newTxt[i] = ((int)utf8Bytes[i]).EncryptInt();
}
}
return newTxt;
}
- 先把來原的資料 .ToString() 轉成 string 後傳到 EncryptString()
- 由於是 string,所以 null 和 "" 零長度都會分開處理
- 如有內容的話,轉成 UTF8 Bytes[]
- 把 UTF8 Bytes[] 中每個 int 逐一傳到 EncryptInt() 中
- EncryptInt() 會把 int × intMultiply + intPlus 後轉成 string
- 然後把每個位數從加密組中隨機取一個加密碼
- 把整個加密後的 string 回傳到 EncryptString()
- EncryptString() 一直把所有 Bytes[] 的加密碼記錄完成
- 最後回傳一個 string[]
在這個加密過程中,一個字母會先變成 256 或 65536 (256,256) 或 16,777,216(256,256,256) 個組合的 UTF8 Bytes,然後每個位數會先再計算後,再隨機加密成 10 個可能性,這裡到底會有多少個可能性出現,我這個數學白癡真的不懂計算了。。。
如果把每行加密組中的加密碼數量加大至 100 的話,真的是無限可能性,因為連重複的機率也差不多沒有了,這樣要在遊戲運行中取得足夠樣本已經是完全不化算的事情了。
解密就不說了,只是反向流程,在下面有原始碼下載,可以自行研究。
<破解風險 Crack Risk>
上面說過,要成功解密就要取得加密機的設計和加密的設定組合,缺一不可。
在這裡可以下載到加密的設計,但設定組合因為可以自行更改,所以最終還是不能被破解。。。這樣想就錯了!
程序在打包時都只是包起來,只要反編譯就可以取得原始碼,也就是加密的設計和設定組合都可以直接取得;不過,解決方法也是很簡單,在 #4 的文章裡也有說過,就是代碼混淆 Obfuscator。
只要有代碼混淆,即使反編譯也沒法看得懂程序,非常可靠,這樣就可以保證設定組合不會外洩了。
<分享 Share>
- Encrypt Method Script 的版本為 v3.22
- Encrypt Group Generator 基本沒有變動
Script 放在 Project 中就可以使用。
使用方法和之前沒有分別,可以看<遊戲開發日誌 #04> 或程序中的說明。
v3.22 版本中把 EncryptFloat() EncryptBool 都刪了,因為已經不會用到。
留言