知物由學 | Lua指令碼保護的前世今生

網易易盾發表於2020-10-13

不少安全專家表示,在網際網路上失去對程式碼的控制,就像把銀行的設計圖交給搶劫犯一樣。


Lua是一種被廣泛用於遊戲開發中的一種計算機語言,方便開發者定製自己所需的功能。其中,紅遍全球的《憤怒的小鳥》就是由Lua語言用Wax開發的。此外,夢幻西遊、奇蹟暖暖、開心消消樂、放置奇兵、最強蝸牛等手遊也採用了Lua語言進行編寫。


知物由學 | Lua指令碼保護的前世今生


近年來,Lua指令碼在遊戲行業長期流行,但Lua指令碼洩露事件屢見不鮮,其安全性也引起關注。不法分子透過開私服、開外掛等途徑,實現對遊戲程式碼的商業變現,這給遊戲開發者造成巨大經濟損失,也危及遊戲玩家包括賬號、物件在內的虛擬資產安全。


本文將聚焦Lua指令碼加密,深入闡述Lua常見的三種加密方式,並探索如何進一步的保護Lua程式碼。


一、背景


“Lua”在葡萄牙語中是“月亮”的意思,1993年由巴西的Pontifical Catholic University開發。


作為小巧的解釋性語言,Lua具有簡單、輕量、易維護的特點,且可以根據自身的特性來模擬物件導向,因此其被嵌入到越來越多的應用中,特別是遊戲中,為遊戲開發帶來了很大的便捷性。例如,Cocos引擎的主流遊戲、U3D遊戲中的熱更框架xlua都會用到Lua語言。


知物由學 | Lua指令碼保護的前世今生


同時,由於Lua語言自身的這些特性,Lua程式碼本身並不安全,很多時候攻擊者可以獲取Lua原始碼進行閱讀,分析,盜用以及篡改等,然後進一步的重打包,給遊戲本身帶來了很大的安全隱患。


二、Lua現有的保護


對於這種指令碼解釋性語言,從程式碼保護的角度跟它自身所表現的形式是密不可分的,對於Lua而言,目前市面上手遊包中可以看到的主要是lua原始碼,luac,luajit三種的表現形式,接下來會詳細的介紹每一種形式以及自身現有的保護以及所暴漏出來的優缺點。


2.1Lua原始碼


目前市面上用到Lua原始碼本身在遊戲中呈現的並不是很多,但是在一些熱更下發中會比較多;因此從原始碼保護的思路會很容易的想到針對Lua原始碼本身進行混淆保護的方案;目前市面上針對Lua原始碼進行混淆的廠家主要有以下這幾家:


  • XFuscator:
  • Luraph:
  • Syanpse Xen:
  • Ironbrew:
  • Verdict: 

對於這種基於原始碼的混淆,優點是是Lua經過處理以後更加的複雜化了,增大了攻擊者進行分析的成本和難度;由於攻防升級,對於上面的混淆也有相對的反混淆處理方式。同時混淆除了自己混淆本身所表現出來的相容性問題以外,對於開發者也有以下這幾個問題:


第一是同一段程式碼的混淆在不同時間進行混淆,所得到的混淆效果是不同的:


由於混淆器為了增大混淆的程度和難度,裡面會有隨機的程式碼要進行熱更,熱更的時候會進行比較,這樣沒辦法進行熱更處理;


第二是針對Lua語法混淆的相容性問題:


由於Lua語法的靈活性,因此去混淆處理的相容性問題比較多;


第三是開發者接入問題:


對於開發者而言進行接入以及出現問題跟第三方進行溝通解決的成本比較大;


2.2luac的形式


  luac是作為自己的語言的位元組碼格式,與其他指令碼語言python等虛擬機器中所表現的出來是一樣的,等Lua載入到記憶體中以後,虛擬機器會載入對應的位元組碼,由於lua主要有5.1、5.2、5.3三個版本,因此也會有對應的三個格式的luac版本,目前在手遊中主流是5.2的版本;


雖然說luac不會以原始碼的形式出現,但是由於Lua位元組碼的執行以及格式可以根據在Lua原始碼中進行探知到,比如luadec反編譯工具,因此luac形式還是不安全的。目前市面上對於這種保護主要有三個形式:


2.2.1:Luac的加密


從Lua的虛擬機器原始碼處可以得知在luaL_loadbuffer函式會載入Lua,因此有安全意識的廠家會對Lua進行加密。修改這個原始碼,在真正的執行前進行解密;


但是由於虛擬機器的執行過程是開源的,並且由於cocos工程編譯處理需要靜態連結對應的引擎庫,這樣對應的引擎so檔案是有符號的,因此對於攻擊者來說,在luaL_loadbuffer函式處可以進行記憶體的DUMP得到正常的位元組碼,然後使用反編譯工具進行處理,進行進一步的修改;


知物由學 | Lua指令碼保護的前世今生



2.2.2:修改Lua虛擬機器中opcode的順序


對於Lua這種解釋性語言,無論是虛擬機器,還是對應反編譯工具都是有一個固定的opcode的順序,有意識的安全廠商會透過修改對應的opcode的順序進行保護,如下圖所示:左邊是正常opcode的順序,右邊是進行隨機化以後的opcode的;


知物由學 | Lua指令碼保護的前世今生


這樣從新編譯處理完以後的luac可以看到如下圖所示:對應的opcode是不一樣的;

知物由學 | Lua指令碼保護的前世今生


opcode不一樣以及對應的解釋順序是如下:

知物由學 | Lua指令碼保護的前世今生


目前對於這種自定義修改opcode的處理方式,目前攻擊者可以根據透過目標虛擬機器載入Lua檔案跟正常虛擬機器編譯的luac進行對比“吐出”對應的對映表,然後進一步的藉助於反編譯工具進行反編譯處理進一步的處理;


或者由於Lua自身的opcode不是很多,如上圖所示可以很輕鬆的定位到正常的執行順序;因此這種處理方式也不是很安全。


2.2.3:對於Lua的虛擬機器執行過程進行保護


可以看到有的遊戲廠商對Lua虛擬機器進行安全編譯處理,也就是複雜化整個虛擬機器的解釋流程,這種做法其實“治標不治本”。


類似如下圖所示:左右是相同功能的一個函式,只是右邊是經過安全編譯器處理的:


知物由學 | Lua指令碼保護的前世今生



對於上面這種處理方式存在的兩個問題:


一是由於Lua本身是開源的,經過安全編譯處理完以後,對應的符號還是存在;攻擊者很容易的定位;


二是對於攻擊者而言其實不用太關心中間的虛擬化解釋執行過程,因此從整個保護的角度來講,實質性作用不大。


2.2.4小結:


目前的以luac為主要表現形式的遊戲廠商主要是對於上面三種保護的綜合使用,但是經過分析可以看到從根本上沒有起到一個好的作用,只能阻止部分初級的攻擊者,對於真正的攻擊點的保護沒有抓住。


2.3luajit的形式


由於考慮到Lua的執行效率問題,luajit誕生了,從名字上可以看出,luajit是Lua的即時編譯器生成的,一個用手寫彙編實現的Lua直譯器和一個可以直接生成機器程式碼的JIT編譯器;根據dynasm動態生成buildvm_xxx.h的檔案,進一步的解釋執行;


目前很多的遊戲廠家,為了進一步的保護遊戲中的指令碼,將Lua處理為luajit的格式,對於luajit而言,也有對應的反編譯工具,ljd或者luajit-lang-toolkit或者luajit-decomp,因此進而一些遊戲廠商在經過luajit形式以後會進行加密處理;


藉助於cocos自帶的加密,大部分的廠商會透過如下設定自己私有的key和sign值;

知物由學 | Lua指令碼保護的前世今生


以及呼叫對應的XXTEA的加密演算法,可以看到經過加密以後得到如下的luajit的編碼形式:

知物由學 | Lua指令碼保護的前世今生

面對上面這種加密的處理方式,解密也非常的簡單:


一是可以使用HOOK在關鍵的函式處進行記憶體DUMP;


二是也可以透過反編譯程式碼,如下圖所示為某知名遊戲對應的key和sign值,然後呼叫XXTEA進行解密可以得到標準的luajit的形式;然後結合反編譯器進行反編譯修改等等;

知物由學 | Lua指令碼保護的前世今生

三、Lua保護的加強


透過上面對於lua、luac、以及luajit的保護以及逆向的角度來看,要想真正的去保護Lua遊戲,可以從以下幾個角度出發:


  • 使用指令碼的保護的演算法的選擇?
  • 對於虛擬直譯器中的符號怎麼進一步的消除掉?
  • 怎麼讓開發者儘可能的接入方便?
  • 對於保護的強度上我們應該怎麼進一步的考慮?

3.1演算法的選擇性


目前很多的遊戲廠商透過Quick-Cocos2dx或者cocos自帶的演算法,以XXTEA這種為代表進行加密處理,包括對於指令碼以及zip包等等加密,這樣使得攻擊者也能夠輕意的去利用這些演算法完成解密等操作;因此演算法的設計越”私有化“越好,這樣可以在第一個層面上做到防止攻擊者進行靜態的還原指令碼。


3.2消除虛擬直譯器中的符號


透過上面可以看到無論是自定義opcode直譯器還是使用安全編譯器處理Lua虛擬機器,這其中存在一個問題,由於靜態連結的問題,符號表是暴漏的,符號表的存在為攻擊者提供了豐富的分析線索 ,因此可以對Lua的虛擬機器解釋引擎進行加殼處理,不僅僅能夠保護上面的私有化解密演算法,同時符號的消除使得攻擊者很難得進一步去分析。做到了第二個層面上的保護,同時有了殼以後會對遊戲周圍的可疑環境進行檢測,比如上面提到的HOOK等。


3.3讓開發者儘可能的接入方便


比如上面提到的對於Lua在混淆處理的時候,儘可能的考慮到開發者的功能業務,是遊戲的邏輯業務還是熱更?否則像上面提到的基於原始碼的混淆,每次的隨機化會導致事倍功半。


3.4保護的強度上應該怎麼進一步的考慮


從上面的分析過程可以看到這裡面我們從以下這幾個角度進行強度上的加強:


一方面,在對Lua原始碼混淆處理的時候,可以對luac以及luajit對應的反編譯工具進行對坑,由於部分攻擊者不是很懂反編譯的原理,加強使得攻擊者不能反編譯;


另一方面,由於Lua自身語法的靈活性,可以對於Lua本身的格式進行自定義化,同時修改對應的直譯器部分,這樣攻擊者就不得不分析自定義的格式以及對應的直譯器部分,加大分析的難度。


綜上所述,如下圖所示:

知物由學 | Lua指令碼保護的前世今生

四、總結


在遊戲開發領域,Lua與C++、C#的組合帶來了十分強大的功能,但也難免存在被破解的風險。


安全攻擊常常以程式碼為目標,達到破解軟體的目的。在導致洩露的網路安全“短板”中,程式碼安全是最本質、最核心的問題。


不可否認的是,在數字經濟時代,對於科技企業而言,程式碼既是著作權的一部分,也是核心商業機密之一。核心程式碼一旦洩露,導致軟體的核心技術外流,這對於企業幾乎是致命的打擊。


駭客在砸殼、逆向之後,“裸奔”的程式碼就面臨全部暴露的風險,增加加密演算法十分有必要。


相關文章