一、遊戲安全評審1.1 遊戲安全評審是什麼
隨著各類遊戲在國內和國際越發火熱,外掛和打金工作室等黑產業務也愈演愈烈;
阻止外掛和黑產的主要手段,從攻擊產生的時間上可以分為:
事先防禦:透過遊戲安全評審,發現遊戲漏洞和可能存在被黑產攻擊的問題並反饋給遊戲業務方完成修復。事中防禦:透過程式碼保護、協議加密、記憶體保護等手段,在遊戲Apk上部署防禦措施,提升黑產活動的攻擊門檻與攻擊代價事後防禦:透過外掛雲查進行外掛分析和打擊,對黑產賬戶和違規玩家進行賬號封禁或其他懲處措施一個優秀的遊戲廠商,會全面發展上述三種技術手段來對抗外掛黑產。在這三者中,事先防禦手段,對於遊戲業務的代價較低,可以在造成實際損失之前從伺服器側解決漏洞問題。
1.2 遊戲安全評審可以解決什麼問題一款遊戲在開發完成上線測試前,可能存在一些日後導致嚴重危害的安全漏洞如:協議漏洞、伺服器宕機、記憶體變速、隱私合規等。
遊戲安全評審就是為了防止遊戲在線上運營階段因以上問題產生安全事件而進行的工作,旨在提前暴露遊戲安全風險,完成漏洞修復,不給外掛黑產以可乘之機。
二、遊戲安全評審技術演進2.1 協議重建&fuzzing方案2.1.1 核心模型
提到協議安全,大家肯定會首先想到協議fuzzing,這也和我們最初的測試思路一致
由於遊戲伺服器和客戶端一般都採用了特殊的格式進行通訊,並且報文內容和報文外部的封裝都進行了複雜的序列化與加密,所以直接向遊戲伺服器傳送大量的fuzz二進位制資料流絕大部分會被伺服器的閘道器過濾掉;因此我們需要對遊戲協議進行重建。
協議重建即在擺脫遊戲操作場景的情況下,對遊戲的資料流進行完全模擬和重新封裝,構建一個合法的遊戲連結,傳送經過fuzz欄位覆蓋的測試協議,透過返回包或者遊戲表現來確定是否有安全漏洞。測試流程如圖所示:
2.1.2 登入態與心跳
遊戲業務對於登入態和存活連結有校驗,所以我們需要根據遊戲校驗邏輯的不同,來執行針對性的登入態和心跳模組。
大部分遊戲要求的登入態都是唯一的,也就是說,在開啟測試工具的同時,原本客戶端上的遊戲登入態會被擠下線,所以對於測試工具新建立的登入態,我們要保證:
登入態的獲取邏輯完全脫離客戶端完整的實現金鑰協商流程按照要求的頻率進行心跳包傳送(是的,心跳包的頻率有時也會影響新建登入態的活性)雖然登入態、金鑰交換、心跳包的維持比較費時費力,但是在這個過程中可以發現很多安全漏洞,例如:不合理的金鑰交換甚至固定金鑰、固定的登入口令、多登入態等嚴重問題。
2.1.3 正確的呼叫鏈
由於遊戲業務在通訊上有別於常規業務的特殊性:很多特殊的協議及其生效判定只有在特定場景和環境下才會生效,可以理解為只有正確的協議鏈條,才會得到伺服器正常的反饋和資料;於是我們在進行如下改進。
先將遊戲客戶端掛代理整體執行一遍記錄下遊戲正常執行過程中的協議呼叫鏈根據呼叫鏈對協議進行重建和自動讀取雖然這個過程較為耗時,但是可以發現很多在遊戲裡不會觸發的隱藏BUG, 這類問題主要是使遊戲程序不按照預定的呼叫鏈來進行通訊,從而產生的多協議組合漏洞。例如在戰鬥中進行跳關和在商店中開啟戰鬥等;
2.1.4 遊戲自研DSL的處理
對於重建協議的方案來說,大部分的模組都是通用的,尤其是協議解析和序列化的部分;但是在個別情況下,除了採用常見的protobuf、json等資料系列化格式外,遊戲還會使用自研私有DSL協議。
針對自研協議格式的遊戲,我們進行了一些研究,發現大部分私有協議格式本根同源,對於將明文資料序列化成二進位制流的關鍵點上的差異不大,於是我們對這些通用的部分進行整合,開發可以通用的序列化與反序列化介面,設定幾個可選引數來適應不同的格式,這樣就可以適配大部分的私有協議了。
OK,到這裡為止,我們就可以對絕大部分接入安全評審的遊戲進行協議安全測試了,但是該方案還存在一些問題,看到這裡,相信有的同學已經發現了這個方案中的一些不足。
2.1.5 方案侷限性
根據評審工作的發展,發現本方案存在的幾個問題:
不能直接在遊戲中看到漏洞的展示效果,在我們傳送一條構造了特殊欄位的測試協議後,預期得到伺服器負面(否定)的反饋,但是不同的遊戲設計的反饋不同,有的遊戲不反饋,有的遊戲只反饋一個錯誤碼,這對於判斷測試欄位是否有效(有效是指伺服器沒有拒絕我的錯誤欄位而將他直接執行了,比如我告訴伺服器,我出生了,我滿級了)是一個很大的障礙(如下圖中展示)
協議接入的時間較久,需要對協議鏈條的各類細節掌握透徹,才可以將工具完美的適配遊戲,而對於一些急於上線的遊戲,這個評審週期可能會影響到發行進度;
對遊戲更新適配性較弱,在我們對一款遊戲完成首次測試後,遊戲的登入態、登入流程、金鑰交換的邏輯會有很大的改變,而協議重建又是個費時的操作,所以這會影響後續的增量測試效率;
這些問題都是由於本方案的侷限性導致,那麼,我們就要考慮在後續的方案中進行升級;
2.2 基於中間人劫持的工具方案2.2.1 核心模型
我們在上一段提到了,新方案主要是為了提升漏洞發現的效率,所以我們首先需要解決的問題是不能直觀的看到漏洞在遊戲中的表現效果。為了達到這個目的,就需要一個可以不脫離客戶端的測試方案:將資料流的起點和終點定位到客戶端和伺服器,而測試工具透過中間人的思想,在通道中進行測試操作,不關心客戶端上的實現和協議鏈。
設計的大體模型如下:
我們使用了一個socks5的代理客戶端,並在測試端上搭建一個代理伺服器,測試邏輯主要執行在代理伺服器中;預期本次設計的方案,其核心架構圖設計如下:
2.2.2 資料修改與重發
現在我們已經可以透過新的工具方案用代理的模式正常的進行遊戲了,並且還可以在工具中看到遊戲的上下行資料流,但是為了達到測試目的,我們還需要將資料管道中的資料拿出來,經過修改後再寫回資料管道,這裡的解析和封裝模組與上一個方案一致,不再詳細展開;
我們透過對本地測試檔案進行改寫,來控制測試的流程和目標,現在我們的新方案已經初步成型了:
2.2.3 協議劫持與主動發起
但是我們目前還有一些問題存在:
中間人方案能進行測試的協議都是由遊戲客戶端發起的,例如購買商品,所以無法繞過客戶端的限制比如金幣不足時無法發起購買,但實際上游戲伺服器中可能存在金幣不足購買不扣費的漏洞;簡而言之,就是隻依賴中間人方案測試覆蓋面不夠:雖然可以覆蓋到全部的遊戲協議,但是不能覆蓋遊戲在特殊情況下存在的問題;
我們希望,可以在任何情況下都可以主動的發起對一條協議的測試,從而繞過客戶端上的邏輯限制,深度挖掘遊戲伺服器中的問題(還是那個原則,客戶端不可信)。這裡就有一個很巧妙的辦法:協議劫持。
而劫持的具體做法是:尋找一條簡單的,不會受到客戶端邏輯限制的協議,比如遊戲裡的動作、表情等。因為這類協議對於遊戲安全來說是冗餘的,安全可靠的。
使用中間人工具進行測試的工作流程如圖所示
2.2.4 方案侷限性
基於中間人的測試方案可以在遊戲客戶端上直接看到bug的展示效果,這很大程度上提升了漏洞的發現效率;但相對於上一代方案來說,還是有兩點問題沒有解決:
協議接入時間較久,因為協議包的解析還涉及到粘包和拆包等問題,較為複雜, 不僅需要對資料層面進行解析,還要對協議層面和資料流層面進行解析。工作量較大。對於新遊戲的二次適配消耗較高,在進行增量測試時,遊戲往往已經根據業務需求和首輪安全評估結果修改了加密和協議方案,該方案很可能和當前接入的協議流程不一致,所以需要對協議流程進行重新的解析,較為耗時。以上問題的產生,都是由於協議解析類方案的侷限性而導致的,想要進一步發展,就需要從根本上拋棄原有的思路。
2.3 基於客戶端注入的劫持和偽造工具(GameDancer)為了解決以往方案中的不足,我們準備對測試方案進行重新設計,結合以往的測試經驗,重新設計的方案的目標是:
1、不在資料流層面進行操作,較少的在協議層面進行操作,直接操作可以看到的明文資料內容。
2、測試協議的元件過程儘量在加解密之前,因為加解密方案是多變的、難以固定適配的。
3、可以直接在客戶端上看到漏洞的展示效果,不需要離線執行。
4、可以方便的主動發起測試協議,用非劫持的方法來向遊戲伺服器發起測試協議,並且不需要對遊戲本身的包序號進行管理。
5、測試工具在前端展示完整解析的協議格式和資料,進行測試資料推薦和fuzz自動覆蓋。
2.3.1 核心模型
為了達到以上的設計目標,我們放棄了原有的協議方案,透過程式碼注入的方式來開發一款新的遊戲安全漏洞挖掘工具——GameDancer。
設計的主要思路是透過hook遊戲邏輯中協議互動的介面,拿到遊戲的互動明文資料,對資料進行解析後,透過主動呼叫遊戲介面,將測試協議透過遊戲客戶端發往遊戲伺服器。
說的簡單一點,對於這套測試方案來講,遊戲客戶端就是一個給我們工具提供協議解析和協議傳送服務的模組,將這部分複雜的工作透過遊戲本身來自動化的完成。其整體的設計模型如下:
2.3.2 前端UI
工具前端是與安全評審人員主要的互動場景,也是決定使用效率的地方。為了提高漏洞的發現效率,我們需要完成以下目標:
針對不同型別的協議欄位進行可能導致BUG的欄位值推薦,減少評估人員填入資料的環節可以對某條或者某幾條協議進行快速自動fuzz的高階傳送功能,一鍵覆蓋可能的問題針對測試環境的不同,提供PC版和網頁版前端介面,PC截圖如下:前端主要和核心框架中的hook引擎進行資料互動,資料互動流程較為簡潔;
2.3.3 載入器和hook引擎
載入模組主要提供注入遊戲的功能,需要將包含遊戲指令碼hook引擎功能的庫檔案載入到遊戲同一個程序和名稱空間下,使我們自定義的遊戲測試邏輯可以注入到遊戲上下文當中,從而對遊戲原生邏輯進行修改和操控;
載入器有兩種技術選型,一種是針對root環境,使用附加Zygote的方式來完成注入;另一種是針對非root環境使用虛擬多開技術進行注入,root模式對測試終端的環境有一定要求,但是其適配的遊戲範圍廣;非root模式不額外需要root環境,但是對於有些遊戲和機型需要特殊的適配。
注入Hook引擎的流程如下:
hook引擎中包含了針對各類遊戲引擎及其技術選型的hook支援,可以對遊戲指令碼進行注入和替換,是該套技術方案中的重點部分。
以Unity3d引擎mono選型為例,主要方案是藉助libmono.so本身的介面和Mono的JIT機制;
首先載入我們自己邏輯的模組,這裡是我們用C#開發的dll檔案。透過mono_class_get_methods 定位到我們要替換的目標方法、替換方法,以及代替原方法的空指標。接下來藉助libmono.so 的mono_compile_method 進行JIT編譯,得到native code 返回地址。完成方法替換,即hook了目標邏輯。2.3.4 邏輯層
邏輯層是控制遊戲執行邏輯和遊戲協議管理的核心,在邏輯層我們需要完成以下預定功能:
版本適配因為遊戲開發商很多,其所使用的Unity的.Net FrameWork版本也不同,高版本的.NET特性無法在低版本執行。由於需要引入遊戲的邏輯,所以想要解析遊戲的指令碼作為動態庫,那麼需要適當的版本來支援解析。協議解析&資料上報因為這裡hook的遊戲介面不同,需要根據不同的情況,將資料解析成明文資料,一般是針對proto和json的解析資料上報即將解析完成的資料以Json的形式傳遞給UI前端,這裡最大的問題在於Json的構建,由於遊戲Unity版本的問題,有的.net版本不支援通用的Json庫,所以需要根據遊戲的執行環境來選擇對應的Json程式碼熱更指令碼互動(Lua)除了在C#層進行hook之外,還需要對遊戲的熱更指令碼進行操作,因為一部分遊戲的主要邏輯存在於熱更指令碼中,例如C#與lua的互動流程如下執行緒監控,持續注入;在一些自定義指令碼引擎中,一段時間後會清空註冊過的指令碼方法,所以需要利用MonoBehaviour等自動建立例項按幀呼叫的方式來監控指令碼方法的註冊情況。回撥收集;由於遊戲的協議設計機制不同,有的遊戲在收到返回包後根據返回包的內容進行頁面渲染,有的遊戲需要透過發包時的回撥來完成對應的狀態改變,所以需要對發包時的回撥進行收集並儲存。需要額外注意公開列舉器IEnumerable與yeild的呼叫流程,這裡的流程不可打斷,不可單獨呼叫。2.3.5 未來展望
GameDancer的開發,並不只是提供了一款快捷、高效遊戲安全評審工具,更重要的,它提供了一個遊戲注入平臺,根據一定的文件指引,相關人員可以開發自己的遊戲外掛來控制遊戲的邏輯走向、觀察遊戲實時效能資料、瞭解遊戲BUG產生的詳細情況;這些也是我們對工具未來發展的期望;
2.4 小結各個方案雖然是一代代的演進過程,但是舊方案並不會被淘汰,每一個技術方案在漏洞挖掘的過程中都有其獨特的用處:基於客戶端注入的方案雖然高效。但是無法發現遊戲加密方案和登入流程在設計上的安全問題,這類問題仍然需要透過協議重建來發現。
在安全評審工作中,我們會採用多型別的工具對遊戲進行多角度的安全評估,保證在評審覆蓋的過程中不留遺漏。
三、結束語“工欲善其事必先利其器”,如你所見,透過技術方案的不斷演進,遊戲安全漏洞的挖掘效率和漏洞質量得到了提升;從最開始為了快速開展安全評估工作的協議重建方案,到後來可以直觀看到漏洞效果的中間人方案,再到GameDancer方案,每一次的方案升級都是為了更好的解決遊戲安全評審業務眼下所存在的問題和侷限。
遊戲安全評審是一項繁瑣的工作,漏洞的種類及其產生條件、原因不一而足,想要儘可能全面的覆蓋一款遊戲中的安全問題,需要評審人員耐心與細緻的檢查,更加需要技術能力和高效工具的支援。在遊戲安全評審技術方案的進階之路上,“追求極致”是激勵我們不斷前進的動力。