日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

给网游写一个挂吧(二) – 启动外挂上

發(fā)布時(shí)間:2024/4/11 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 给网游写一个挂吧(二) – 启动外挂上 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前面的文章給網(wǎng)游寫一個(gè)掛吧?–?反反外掛驅(qū)動(dòng)的驅(qū)動(dòng),我們已經(jīng)可以訪問(wèn)游戲的內(nèi)存之后,接下來(lái)需要:

1.?????????找到游戲里關(guān)鍵元素的偏移量,比如生命值的內(nèi)存的位置。一般來(lái)說(shuō),大部分大型3D游戲都是用C++編寫的,游戲里面的元素都是面向?qū)ο蟮?#xff0c;比如玩家是一個(gè)對(duì)象,那么生命值、魔法值之類的東西都是這個(gè)對(duì)象的一個(gè)屬性。按照C++的內(nèi)存布局,一般來(lái)說(shuō),只要源代碼里的結(jié)構(gòu)體不發(fā)生變化,屬性的偏移量一般來(lái)說(shuō)都是一樣的。

2.?????????找到游戲里一些關(guān)鍵函數(shù)的地址,便于外掛程序來(lái)調(diào)用。

?

查找關(guān)鍵元素的偏移量和關(guān)鍵函數(shù)地址一般來(lái)說(shuō)都是苦力活,當(dāng)然也是智力活,需要你的逆向工程水平不錯(cuò),網(wǎng)上有些相關(guān)的教程,這里我就不再詳述了。

?

這里假設(shè)我們已經(jīng)找到游戲的偏移量了,現(xiàn)在的問(wèn)題是如何啟動(dòng)外掛以操控游戲,一般來(lái)說(shuō)有幾種選擇:

1.?????????要么是內(nèi)掛,將掛注入到網(wǎng)游進(jìn)程的內(nèi)存空間里,這樣掛就相當(dāng)于網(wǎng)游自己的一個(gè)組件,對(duì)游戲進(jìn)程擁有絕對(duì)的訪問(wèn)權(quán),可以讀寫游戲的虛擬內(nèi)存地址以及調(diào)用游戲內(nèi)置的函數(shù)。這種做法的弊端是,如果游戲有非法組件檢測(cè)線程的話,很有可能被發(fā)現(xiàn)。

2.?????????要么是外掛,將掛作為一個(gè)獨(dú)立的進(jìn)程,這樣掛可以通過(guò)Read/WriteVirtualMemory來(lái)讀寫游戲的內(nèi)存,再通過(guò)CreateRemoteThread API啟動(dòng)一個(gè)遠(yuǎn)程線程來(lái)調(diào)用游戲內(nèi)置的函數(shù)。這種做法可以查看文章:代碼注入的三種方法。

?

那本文我們講解第一種方法?-?內(nèi)掛。并針對(duì)兩款游戲來(lái)說(shuō)說(shuō)注入內(nèi)掛的方法:

?

DNF –?使用輸入法注入技術(shù)

輸入法注入技術(shù)的原理是,寫一個(gè)輸入法DLL并在系統(tǒng)中注冊(cè),然后向游戲發(fā)送一個(gè)切換輸入法的消息?–?當(dāng)然是切換到我們寫的輸入法,Windows會(huì)加載我們的輸入法DLL,在這個(gè)DLL的DllMain函數(shù)里,我們就可以完成一些內(nèi)掛加載以及初始化的工作:

1.?????????首先寫一個(gè)輸入法DLL,隨便從網(wǎng)上下載一個(gè)示例用的輸入法源碼即可。

2.?????????在輸入法DLL的DllMain函數(shù)的DLL_PROCESS_ATTACH事件中,啟動(dòng)外掛線程。

3.?????????在單獨(dú)的外掛進(jìn)程里?–?一般來(lái)說(shuō)這個(gè)進(jìn)程就是用來(lái)給外掛用戶操作的一個(gè)Windows GUI程序,在合適的地方:

a)?????????用imm32.dll里的ImmInstallIMEw API函數(shù)在系統(tǒng)里注冊(cè)我們的輸入法。

b)?????????用FindWindows API查找到所有需要注入的窗口,這里就是獲取DNF的窗口句柄。

c)?????????最后用PostMessage WM_INPUTLANGCHANGEREQUEST消息強(qiáng)迫Windows針對(duì)DNF窗口切換我們的輸入法,從而達(dá)到加載內(nèi)掛的目的。

?

關(guān)鍵代碼如下?–?整個(gè)程序大部分代碼都是用C#完成,稍后介紹選用C#的原因:

?

輸入法注入代碼C#部分:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

public?bool?InjectDllToWindow(string?dllPath,?string?windowText =?"地下城與勇士",

string?classText =?"地下城與勇士",bool?IfMonitor=true)

{

??? InjectedDll = dllPath;

??? WindowText = windowText;

??? ClassText = classText;

?

????// 1,?注冊(cè)輸入法

??? HKL = RegisterIME();

????if?(HKL ==?IntPtr.Zero)

??? {

????????MessageBox.Show(string.Format("GetLastError: {0}", GetLastError()));

????????//2,如果注冊(cè)失敗,檢查是否已經(jīng)被注冊(cè)

??????? HKL = MImeFindByName();

??? }

?

????if?(HKL ==?IntPtr.Zero)

??? {

??????? isRegister =?false;

????????return?false;

??? }

??? isRegister =?true;

?

????//3,把需要注入的dll傳遞給服務(wù)輸入法dll

??? IMESetPubString(dllPath, 0, 0, 0, 0);

?

????//4,查找所有需要注入的窗口

????List<IntPtr> windowsToInject = FindWindows(classText, windowText);

?

????//5,注入輸入法到窗口

????foreach?(IntPtr?window?in?windowsToInject)

??? {

??????? InjectToWindow(window);

??? }

?

??? WindowsHaveInjected = windowsToInject;

?

????if(IfMonitor)

??? {

????//6,開(kāi)啟監(jiān)視線程,監(jiān)視新的窗口,一旦開(kāi)啟,立刻注入

????????WorkThread?thread =?new?WorkThread(MonitorDNFWindow);

??????? workThreadAsyncResult = thread.BeginInvoke(null,?null);

??? }

????return?true;

}

?

private?IntPtr?RegisterIME()

{

????string?tempDir =?Environment.CurrentDirectory;

????Environment.CurrentDirectory =?Environment.SystemDirectory;//把工作目錄切換到系統(tǒng)目錄

????IntPtr?hkl = ImmInstallIMEW(ImeName, ImeFriendlyName);?//安裝服務(wù)輸入法

????Environment.CurrentDirectory=tempDir;?//切換回原目錄

????return?hkl;

}

?

private?void?InjectToWindow(IntPtr?hWnd)

{

??? PostMessage(hWnd, WM_INPUTLANGCHANGEREQUEST, (IntPtr)0x01, HKL);

}

?

在上面第9行調(diào)用47 – 54行的函數(shù),將外掛的工作目錄切換到系統(tǒng)目錄,因?yàn)槲覀儗⑤斎敕ǚ诺较到y(tǒng)目錄,方便系統(tǒng)查找,并安裝輸入法。

?

第25行里,設(shè)置在輸入法注入成功后,需要執(zhí)行的操作,一般來(lái)說(shuō)就是啟動(dòng)掛了。有些內(nèi)掛會(huì)在注入成功后,注冊(cè)一個(gè)快捷鍵,通過(guò)快捷鍵呼出一個(gè)窗口,這個(gè)窗口可以用來(lái)跟用戶操作界面通信,執(zhí)行操作界面來(lái)的命令。然而,在某些游戲里,呼出的窗口會(huì)馬上被檢查到,我們這里將介紹在游戲進(jìn)程里啟動(dòng).NET程序,啟動(dòng)一個(gè).NET Remoting服務(wù)的方式。

?

第31 - 34行,通過(guò)FindWindows系統(tǒng)調(diào)用枚舉系統(tǒng)上的窗口,找到目標(biāo)窗口,執(zhí)行注入操作,具體的注入操作參見(jiàn)56 – 59行的代碼。在.NET代碼里調(diào)用C/C++函數(shù)的方式,請(qǐng)參閱文章:使用Signature Tool自動(dòng)生成P/Invoke調(diào)用Windows API的C#函數(shù)聲明。

?

輸入法C++部分關(guān)鍵代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)

{

???switch(fdwReason)

??? {

??????case?DLL_PROCESS_ATTACH:

????if?(CilentDLL==NULL)

??? {

??????? ??if?(lstrlen(g_IMEDLLString)>0)

??????? ? {

??????????? ? StartTheDotNetRuntime();

??????? ? }

??? }

??????? ??break;

??? ??case?DLL_THREAD_ATTACH:

??????? ?break;

??? ??case?DLL_THREAD_DETACH:

??????? ?break;

??????case?DLL_PROCESS_DETACH:

????????break;

??????default:

????????break;

??? }

????return?true;

}

?

DWORD CALLBACK StartTheDotNetRuntime(LPVOID lp)

{

??????? HRESULT hr = S_OK;

??????? ICLRMetaHost??? *m_pMetaHost = NULL;

??????? ICLRRuntimeInfo *m_pRuntimeInfo = NULL;

??????? ICLRRuntimeHost??? *pClrHost = NULL;

??????

hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*) &m_pMetaHost);

if?(hr != S_OK)

????return?hr;

hr = m_pMetaHost->GetRuntime (L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID*) &m_pRuntimeInfo);

if?(hr != S_OK)

????return?hr;

hr = m_pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*) &pClrHost );

???????if?(FAILED(hr))?return?hr;

?

??? hr = pClrHost->Start();

?

??? DWORD dwRet = 0;

??? hr = pClrHost->ExecuteInDefaultAppDomain(

??????? g_IMEDLLString,

??????? _T("ManagedDll.Program"), _T("Start"), _T("nothing to post"), &dwRet);

?

??? hr = pClrHost->Stop();

?

??? pClrHost->Release();

?

????return?S_OK;

}

?

在第10行代碼,輸入法注入成功后在游戲進(jìn)程里啟動(dòng).NET虛擬機(jī),這里啟動(dòng)的4.0的運(yùn)行庫(kù)?–?參看36行代碼,虛擬機(jī)成功啟動(dòng)后,會(huì)返回一個(gè)ICLRRuntimeHost的COM接口,根據(jù)這個(gè)接口,外掛就可以創(chuàng)建托管代碼運(yùn)行需要的應(yīng)用程序域?–?參看45 – 47行。在應(yīng)用程序域里執(zhí)行代碼并不需要一個(gè).exe的可執(zhí)行文件,只需要是一個(gè)托管程序的DLL文件,這個(gè)DLL文件需要放在游戲的目錄里,因?yàn)槲覀兊膾焓沁\(yùn)行在游戲的進(jìn)程里,工作目錄也自然變成了游戲的工作目錄了。

?

在47行,我們可以看到,可以指定DLL內(nèi)部任意一個(gè)類型的靜態(tài)函數(shù)作為入口點(diǎn),下面是ManagedDll.Program.Start的源代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

namespace?ManagedDll

{

?????public?class?Program

??? {

????????static?int?Start(string?argument)

??????? {

??????????? RemotingServer.Start();

????????????while?(true)

??????????? {

????????????????Thread.Sleep(1000);

??????????? }

????????????return?0;

??????? }

??? }

}

?

在第7行,我們啟動(dòng)了一個(gè).NET Remoting服務(wù)(或者說(shuō)是web服務(wù),因?yàn)?NET Web服務(wù)本來(lái)就是基于Remoting的),等待任意一個(gè)地方的Remoting客戶端鏈接……對(duì)于在非托管進(jìn)程當(dāng)中啟動(dòng)托管程序的方法,詳情請(qǐng)參看:將托管dll注入到非托管進(jìn)程中。

?

設(shè)計(jì)考量

?

之所以選用C#的原因是:

1.?????????可以快速編程,而且有豐富的類庫(kù)。

2.?????????垃圾回收機(jī)制可以增強(qiáng)掛的穩(wěn)定性,而且也不用考慮內(nèi)存泄露的問(wèn)題。

3.?????????有很強(qiáng)大的調(diào)試工具,以我經(jīng)驗(yàn)來(lái)看,暫時(shí)還沒(méi)有看到比VS更強(qiáng)大的調(diào)試工具。

4.?????????最后,在游戲進(jìn)程里的內(nèi)掛和外部供用戶配置的GUI程序需要通信,沒(méi)有比.NET Remoting更方便的東西了!

?


最后如果大家對(duì)調(diào)試技術(shù)感興趣的話,可以考慮購(gòu)買我的新書:?應(yīng)用程序調(diào)試技術(shù),這套視頻除了講解調(diào)試的技巧外,還盡量完整地講解了周邊用到的技術(shù),這是因?yàn)檎{(diào)試技術(shù)要好的話,需要基礎(chǔ)功和背景知識(shí)扎實(shí)才行。 未完待續(xù)……

總結(jié)

以上是生活随笔為你收集整理的给网游写一个挂吧(二) – 启动外挂上的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。