输入法注入源码_将注入进行到底:利用Mono注入C#游戏脚本
本文作者01dTan9,首發(fā)于三葉草小組博客:http://blog.sycsec.com/?? 致力于引領(lǐng)每一位對windows安全感興趣的萌新,希望讓萌新能夠?qū)indows有初步的了解。筆者的文章涉獵廣泛,從Windows基礎(chǔ)編程,加密與解密,跨平臺腳本語言,硬件安全,到游戲引擎逆向,涵蓋多方面,多領(lǐng)域的理論與技術(shù),幫助萌新更快地從入門到入獄。
?1. 游戲腳本是什么?
腳本語言(英語:Scripting language)是為了縮短傳統(tǒng)的“編寫、編譯、鏈接、運行”(edit-compile-link-run)過程而創(chuàng)建的計算機編程語言。早期的腳本語言經(jīng)常被稱為批處理語言或工作控制語言。一個腳本通常是解釋運行而非編譯。
游戲腳本由于腳本語言的開發(fā)成本低,許多游戲引擎不約而同地使用了腳本語言作為主要編程語言,比如大名鼎鼎的《魔獸世界》,就是使用的Lua進行開發(fā)的。本文所描述的游戲腳本是Unity(游戲引擎)中使用的主要編程語言——C#,并且Unity提供C#中的主要腳本API ,其腳本具有簡單、易學(xué)、易用的特性,目的就是希望能讓開發(fā)人員快速完成游戲開發(fā)。
2.為什么要注入游戲腳本?
1.?傳統(tǒng)注入方式
對于傳統(tǒng)的第三方游戲插件開發(fā)而言,由于沒有游戲廠商提供的官方接口,只能通過非常規(guī)的方式將插件的功能安裝在游戲中。
通常來說,這些安裝的方式與病毒的行為十分類似,更偏向于系統(tǒng)底層,比如利用Windows API修改目標進程中的數(shù)據(jù),或者創(chuàng)建遠程線程讓目標進程執(zhí)行匯編代碼或加載第三方庫(如DLL),更有甚者,直接在Windows內(nèi)核中劫持目標進程網(wǎng)絡(luò)通訊,或進行APC注入。
目前傳統(tǒng)的應(yīng)用層注入方式主要有以下幾種:
靜態(tài)注入類:
? ? ? ? 導(dǎo)入表注入
? ? ? ? 注冊表注入
? ? ? ? DLL劫持注入
動態(tài)注入類:
? ? ? ? ?鉤子注入
? ? ? ? ?APC注入
? ? ? ? ?遠程線程注入
? ? ? ? ?輸入法注入
? ? ? ? ?LSP注入
使用上述方法安裝的插件,其功能實現(xiàn)大部分要靠硬編碼、鉤子、內(nèi)存修改來實現(xiàn)。這樣一來,需要耗費大量的時間分析游戲的匯編代碼與通過內(nèi)存中的數(shù)據(jù)來構(gòu)建結(jié)構(gòu)體與游戲?qū)ο蟆?/p>
2. 從硬編碼注入到腳本注入
假如我們可以讓游戲腳本在游戲中執(zhí)行,通過腳本直接使用游戲引擎封裝好的函數(shù)與對象,不就可以事半功倍了嗎?
3. Mono 與 Unity 下的C#游戲腳本
1.?Unity3D
Unity3D是由Unity Technol ogies開發(fā)的一個讓玩家輕松創(chuàng)建諸如三維視頻游戲、建筑可視化、實時三維動畫等類型互動內(nèi)容的多平臺的綜合型游戲開發(fā)工具,是一個全面整合的專業(yè)游戲引擎。Unity使用戶能夠以2D和3D方式創(chuàng)建游戲,并且引擎提供C#中的主要腳本API ,用于插件形式的Unity編輯器,游戲本身以及拖放功能。在?C#作為引擎使用的主要編程語言之前,Unity支持?Boo?和?JavaScript?版本的?UnityScript。
2. Mono
Mono是一個免費的開源項目。由微軟的子公司Xamarin(前身為Novell,最初由Ximian)和.NET基金會領(lǐng)導(dǎo)。旨在構(gòu)建符合ECMA(歐洲計算機制造商協(xié)會)標準的.NET Framework兼容工具集。包括?C#編譯器和帶有實時(JIT)編譯的公共語言運行時。
3.?Mono和Unity3D
簡單來說,Mono是Unity3D的一個運行時,負責C/C++和C#/CIL的交互。
舉個例子,隨便打開一個Unity游戲的根目錄,你會發(fā)現(xiàn)一個名為UnityPlayer.dll的動態(tài)鏈接庫,這個動態(tài)鏈接庫封裝是Unity的底層C++代碼。
在?游戲根目錄\Mono\EmbedRuntime\?(某些低版本Unity游戲目錄為游戲根目錄\Mono\,筆者的為 2018.2.0 Beta)下有一個名為mono.dll的動態(tài)鏈接庫,這個便是負責C/C++和C#/CIL的交互的模塊,網(wǎng)上有開源版本,但許多游戲公司為了防止游戲被惡意修改,通常都重新編譯mono.dll,在其中加入加密游戲數(shù)據(jù)與檢測惡意行為的代碼。
在游戲根目錄\游戲名_Data\Managed\中名為Assembly-CSharp.dll的文件便是C#腳本代碼。
三者的關(guān)系如下:
4.如何利用?Mono平臺實現(xiàn)游戲腳本入?
利用Mono平臺實現(xiàn)游戲腳本注入的主要方法有兩種,一種是靜態(tài)修改,直接PatchAssembly-CSharp.dll的代碼。
本文測試環(huán)境是在Windows下,主要是闡述思路,Android平臺的注入同理。
另一種是調(diào)用mono.dll的API,動態(tài)加載C#代碼(移動端同理),本文主要討論后者。
代碼實現(xiàn)主要由三部分組成:
? ? C#庫文件(負責實現(xiàn)第三方功能)
? ? 動態(tài)鏈接庫(用于在目標程序加載腳本)??
? ? 主程序(負責注入DLL到目標程序)
1.C#庫文件
負責實現(xiàn)第三方功能,代碼需要因游戲而定,主要是通過獲取或修改Unity原生組件(Transform、physics等)的數(shù)據(jù)實現(xiàn)第三方功能,也可以直接使用開發(fā)者定義的函數(shù),但這種方法需要獲取游戲源代碼,需要解密腳本文件,在此不再贅述。
2.動態(tài)鏈接庫
在目標進程中載入C#腳本需要以下API,具體參數(shù)查閱mono官方文檔,上面都有詳細解釋。
通過文件名加載C#腳本文件鏡像。
????????mono_image_open_from_data
從文件鏡像中讀取并將C#代碼編譯為IL代碼,至此,C#代碼被即時編譯完畢。
????????mono_assembly_load_from_full
獲取IL代碼的鏡像。
????????mono_assembly_get_image
通過類名獲取類句柄,供????????????????????????mono\_class\_get\_method\_from\_name調(diào)用。
????????mono_class_from_name
通過類句柄和函數(shù)名獲取函數(shù)地址,供mono\_runtime\_invoke調(diào)用。
????????mono_class_get_method_from_name
通過函數(shù)地址運行IL代碼,代碼開始運行。
????????mono_runtime_invoke
在調(diào)用mono的API之前,我們需要獲取API的函數(shù)地址,基本方法如下:? ?
首先獲取mono.dll的模塊句柄
HMODULE hMono = GetModuleHandle(L"mono.dll");
再獲取API地址
typedef void* (__cdecl *MONO_IMAGE_OPEN_FROM_DATA)(char *ImageName);
MONO_IMAGE_OPEN_FROM_DATA mono_image_open_from_data;
mono_image_open_from_data = (MONO_IMAGE_OPEN_FROM_DATA)GetProcAddress(hMono, "mono_image_open_from_data");
用這種方法獲取所有需要調(diào)用的API
加載C#腳本代碼
#define ClassName L"ClassName" #define MethodName L"MethodName" #define name_space L"name_space" intptr_t raw_image = ImageOpenFromDataFull(file_data); intptr_t assembly = AssemblyLoadFromFull(raw_image); intptr_t image = AssemblyGetImage(assembly); intptr_t class_id = GetClassFromName(image, name_space, ClassName); intptr_t method = GetMethodFromName(class_id, MethodName); RuntimeInvokeMethod(method); |
還可以在DLL中實現(xiàn)C#熱更新,方便調(diào)試。
3.主程序
主程序負責將動態(tài)鏈接庫注入到目標程序。
通過上文提到的遠程線程注入方法
使用?CreateRemoteThread?遠程調(diào)用?LoadLibrary?即可。
5. 腳本注入攻防思路
1 . 如何對抗腳本注入(Anti-Cheat)
對抗腳本注入,有以下幾種、主要思路是在mono.dll上做手腳。
? ? ? ? ·當前流行的方法通過修改mono源碼,加密Assembly-CSharp.dll,并在mono_image_open_from_data里加入解密腳本的函數(shù),這樣既可以防止腳本被靜態(tài)Patch,又可以防止動態(tài)注入。
? ? ? ? ? ·Android平臺下的進程保護
? ? ? ? ??·Windows平臺下的進程保護(R0、R3)
另外一種是在其他mono函數(shù)加入檢測代碼。筆者所遇到便是在mono_class_from_name加入檢測代碼,非原生腳本的加載都會導(dǎo)致函數(shù)調(diào)用崩潰。
2.?如何繞過防注入(By Pass)?
·利用沒有被修改的函數(shù)加載代碼,如mono_assembly_foreach枚舉IL代碼鏡像,繞過mono_class_from_name的檢測。
·重寫mono_class_from_name函數(shù)。
·加載另一個純凈的mono.dll,使用純凈模塊里面的函數(shù)來加載代碼。
6.總結(jié)
利用mono平臺注入C#代碼大大提高了開發(fā)效率,如果是單純用原生Unity組件開發(fā)出來的代碼基本上適用于所有用Unity引擎開發(fā)的游戲。
未來Unity可能會放棄mono平臺,轉(zhuǎn)用LICPP平臺,但是短時間內(nèi)是不會放棄mono這個成熟的平臺的,所以,本方法在未來一段時間內(nèi)都有利用價值。
點擊閱讀原文查看更多
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的输入法注入源码_将注入进行到底:利用Mono注入C#游戏脚本的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python翻译成计算机是啥_基于Pyt
- 下一篇: php +号在传输参数的过程中被变为空格