常常聽到某某遊戲外掛猖獗,作弊仔沒品、破壞遊戲樂趣等等行徑。身為一個好奇寶寶,總是很想知道其中奧秘,雖然我沒有厲害到開發出什麼外掛程式,但這幾年也陸續發現多款遊戲設計上的缺陷,成功用非正當的方式遊玩遊戲 (好含蓄…XD)。看著看著也發現其實這些遊戲的開發者理論上有辦法防止這類型的攻擊,所以只好用我不專業的見解寫下這篇文章。
對沒錯我就是奧客玩家!
本文提及的遊戲只代表曾經可以作弊,不代表現在或未來也可以。
說到這幾年陸續看到有隙可趁的遊戲,方法不外乎是兩個,一是藉由封包攔截查看內容再藉由修改封包資訊重送來攻擊,二是直接更改記憶體位置的值,這兩個方法都是非常直接暴力,案例很多,容我一一介紹。
Metal Slug Attack
其實這遊戲我已經寫過一篇文章了,但容我再次鞭屍 🤣
這遊戲是十分經典只要會察看網路封包,一定有辦法使用重送攻擊。到底什麼是查看封包再重送呢?來看看他的遊戲模式:基本上是關卡制塔防遊戲,勝利可以拿到獎勵。所以當我攔截到開始跟結束的封包訊息之後,我就可以直接自動刷關了。
1 | echo "Start event level=HELL......$i" |
可以看到所有的參數都是透過明文傳輸,唯一的保護是有使用 https,但這樣並無法防止封包被查看,因為只要建立 Proxy server 並自己簽署一個 SSL 憑證,還是可以偷看 https 的封包。
那如果我是開發者我會怎麼改善呢?比較基本的是加上 nonce
在 request 裡面並加密參數。nonce
本身是一個遞增數字,伺服器端須檢查 nonce
值要是遞增的不然就非合法。這種作法常見於加密貨幣交易所的 API 設計,這也是防止重送攻擊常見的手段。[1]
遊戲王 Duel Links
曾經風靡一陣子的遊戲王卡牌遊戲,同時也是伺服器設計有嚴重缺陷,嚴重到我覺得他們工程師可以東西收一收的程度。
這遊戲我開服三天就把全腳色練到滿等,同時集齊當時所有 UR SR 卡片,兩個禮拜後覺得太無聊直接棄坑,堪稱我玩過時間最短的遊戲。
其實他們犯了常見的錯誤,就是所謂 Atomic 操作原則。
有些事情是必須一起完成的,否則就應當全部都不算數。比如說:「我用十元買了一枝筆」,那我的錢包應該要減少十元,並且增加一枝筆。這其中每個動作缺一不可:如果錢包沒扣錢,則整段交易應該都要失敗並回復到原本的狀態。這很基本很合理。
此遊戲一樣是遊玩扣體力的機制,藉由有限體力來使一般人無法狂練等。但查看封包後發現,他開始關卡跟扣體力是兩個不同的 API,是透過遊戲先發送開始再發送扣體力的訊息,這樣一來我只要一直狂重送開始但不送扣體力,我就可以無限遊玩刷等。真的太扯了…..
另外在設計 server side service 時,有一個大原則就是不可以相信客戶端送來的資料,永遠必須做驗證。
而且客戶端不該發送結果,應該只發送欲執行的操作。用剛剛的例子來說:
「我用十元買了一枝筆,我的錢包減少十元,並且增加一枝筆。」→ 由客戶端計算結果是不可以的。
「我要用十元買一枝筆。」→ 只發送欲執行的操作,實際結果應由伺服器端執行,才能確保合法性。
唉,本遊戲真的是奇觀。
Messanger 籃球、足球
Messanger 兩年前推出可以在聊天室內玩籃球的彩蛋,浪費了我無數個小時在那邊投籃,其實也是有辦法作弊的。
前面提到我目前知道的方法不外乎是藉由封包攔截重送來攻擊或是直接更改記憶體位置的值,facebook 在中間人攻擊下了不少工夫,至少就我的觀察只要使用任何 Proxy server 似乎都會無法正常使用 Messanger,所以封包攔截重送這條路就很難走了。
剩下試試直接更改記憶體位置吧,基本上原理就是掃過全部此應用程式用到的記憶體位置並尋找指定的數值。比如說投籃好了,投進 1 分時先尋找記憶體中數值等於 1 的,再投進一次就進一步搜尋數值等於 2 的記憶體位置,以此類推直到精確地找到代表分數的記憶體位置,再修改其值。
很遺憾的是如果遊戲是單機遊戲,基本上是不可能防止記憶體竄改的[2],只能讓他變更難修改沒辦法完全防止。比較常見的做法是增加一個 dirty check 的檢查值,比如說分數的平方。在這樣的設計下,當你的分數被竄改成
不過其實這類的防禦性設計有時候根本不見得需要,以我上述提到的例子而言,除了遊戲王缺陷太嚴重以外,其實其他的都不見得需要做改善。像是 Metal Slug Attack 無法做到數據的篡改,能做到的只有自動花體力刷關;而 Messanger 的遊戲根本是類單機遊戲,就算拿高分也只是自爽。
所以到頭來,最有趣的還是尋找系統缺失的過程吧…😅