主頁 > 後端開發 > 對反游戲外掛技術的思考及實作

對反游戲外掛技術的思考及實作

2021-09-05 08:16:57 後端開發

前言

現如今,有很多游戲外掛軟體,它們可以修改游戲顯示的資料和內部代碼,來達到謀取利益的目的,在實作反外掛技術前,我先介紹一下常見的三種外掛:
1.模擬類外掛:該外掛可以說是最早的外掛了,它是往游戲發送偽造的按鍵訊息來模擬人的手工操作,實作的思路為:

1.在Ring3層使用SendMassage、PostMassage、keybd_event、mouse_event等向訊息佇列中發送按鍵訊息

2.使用回呼函式KeyboardClassServiceCallback和MouseClassServiceCallback往類驅動輸入摁鍵訊息,

2.內部Call呼叫外掛:使用逆向技術分析出游戲內部的匯編代碼,把里面對游戲玩家有利的函式作為外掛的實作功能,實作的思路為:

1.使用逆向工具,去除殼和里面的反除錯功能

2.分析游戲的使用程序、玩家自身的資訊、背包資訊、敵人資訊等

3.逆向分析出游戲的明文發包功能

4.根據游戲的明文發包函式和報文資訊分析游戲內部功能實作的函式

3.脫機外掛:分析出游戲客戶端和服務端之間的通信邏輯,使用自己發送和接受資料來跟服務端進行互動,實作的思路如下:

1.使用逆向工具,去除殼和里面的反除錯功能

2.逆向分析游戲中的匯編代碼,分析出加解密演算法及各種資源資訊

3.分析出登錄封包,獲取各種跟游戲相關的資訊

4.整合尋路演算法,實作掛機

保護call函式和基址資料

1.最近玩植物大戰僵尸和連連看等游戲的時候發現,這些游戲需要通過啟動程式才能正常運行,那么我突發奇想,如果在程式運行前,修改游戲中的部分代碼段資料,等需要運行游戲時再通過另一個程式恢復代碼段資料,那么就可以起到保護游戲的作用,

2.對于游戲外掛制作者,他們經常習慣性的使用OD工具和CE工具、ida工具等,找出游戲的基址資料(也為全域變數)和內部call,然后修改基址資料或者修改執行流程,呼叫內部call,那么在游戲運行前就對基址資料地址和某些call后接地址進行修改,等需要運行游戲時,再修改回基址地址和call呼叫的地址,就可以起到反外掛的作用,

3.接下來,介紹一下這樣做的作用:

4.簡單介紹一下實作的流程:

1.讀取PE檔案資訊到記憶體中,遍歷PE檔案中的代碼段,找到函式頭部地址和使用的全域變數地址;

2.在代碼段中,找出呼叫函式的call指令,把call后接地址進行修改,對于資料,找到使用全域變數的mov指令,修改地址運算元;

3.運行時,使用CreateProcess函式創建執行緒并掛起,然后恢復代碼段中的資料并運行行程

5.我開發了一款反游戲外掛工具(該工具及其原始碼可在文章末尾下載)來模擬這個程序,在檔案選擇框選擇要保護的PE檔案,把需要保護的函式及資料添加到左邊欄中,執行保護時,在PE檔案目錄下會生成已保護的PE檔案,點擊運行時,就能正常運行,

檢測代碼段

1.在游戲沒有運行之前,修改、添加或洗掉PE檔案的二進制資訊可以起到修改程式流程的作用,從而起到外掛的作用,實作的技術有:在代碼段的空白區域添加"洞穴代碼",修改OEP的值,讓程式跳轉到洞穴代碼;使用Inline HOOK執行外掛功能函式;修改代碼段中的跳轉指令和call后接地址,執行游戲內部匯編指令或者調動內部call等

2.在游戲運行時,通過外掛工具動態修改、添加或洗掉行程代碼段資料或者動態注入shellcode同樣也能起到修改程式流程的作用,那么需要校驗PE檔案和游戲記憶體資料就可以檢測出游戲外掛,

3.在實際運用中,可以計算出PE檔案的校驗值,存放在游戲第三方程式的資料段中或者存放在游戲的服務器端,那么在游戲運行前校驗PE檔案值和運行時動態檢驗行程記憶體,接下來使用工具來模擬這個程序:

1.選擇PE檔案計算PE檔案校驗值和記錄代碼段資料;

2.選擇需要校驗的檔案,同樣也計算出校驗值并比較是否變化;

3.選擇正在運行的行程,比較代碼段資料是否變化,

4.實作的思路為:

1.使用MD5演算法計算出前后PE檔案的校驗值,并進行比較;

2.先使用陣列存放PE檔案代碼段資料,后讀取行程記憶體中的代碼段資料,然后比較陣列資料是否相同

5.在游戲運行時,把游戲運行程序中使用過的函式及其相關的暫存器資訊存放在服務端,在服務端檢測函式的呼叫流程,就可以判斷游戲內部call有沒有非法呼叫,不過我這里模擬這個程序的方式是把函式運行時的資訊寫到日志檔案中,查看日志資訊來判斷游戲內部call是否被非法呼叫,

6.實作的思路為:

1.調式行程,回圈接受調式事件;

2.在剛開始調式時,把函式頭部修改為CC;

3.當發現中斷時,判斷發生中斷的地址是否為函式頭部地址,是的話,把執行緒背景關系資訊記錄在日志檔案中

//調式行程
DebugActiveProcess(pid):                           
//回圈等待調式事件                           
while (WaitForDebugEvent(&de, INFINITE))                               
    {                               
    ......                               
    PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;                               
    //觸發創建調式行程事件時                               
    if (CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode)                               
        //修改函式頭部                               
        SetUserFunc(&de);                           
    //觸發例外事件時                               
    else if (EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode)                               
        //判斷觸發例外地址                            
        if (OnExceptionDebugEvent(&de))                           
            continue;                       
    //繼續接受調式事件                               
    ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);                               
 
    }

HOOK檢測

1.現在,有各種各樣的HOOK技術,包括 InLine HOOK、IAT HOOK、API HOOK等,其中大部分需要注入dll來實作HOOK功能,那么游戲運行時,服務端或者游戲第三方程式可以檢測有無dll注入,有的話,及時清除掉,實作的思路為:

1.先記錄游戲運行程序中用的dll檔案名;2.游戲運行時遍歷行程使用的dll檔案名,若遇到不名dll檔案名,則進行釋放;

2.使用工具模擬這個程序:選擇行程,選中dll檔案所在目錄,點擊檢測dll,就可檢測出行程中是否有dll注入,

3.那么,除了檢測dll檔案外,還可以檢測 IAT表和函式內部代碼的跳轉指令來判斷游戲中是否有IAT HOOK或者InLine HOOK(跟API HOOK差不多,范圍比API HOOK小,我這里就不介紹了),實作的程序為:

1.選中游戲PE檔案后,定位到匯入表目錄,遍歷INT表中的_IMAGE_THUNK_DATA,通過里面的聯合體u1,來獲取游戲運行后IAT表中的函式地址;

2.選中行程,讀取行程記憶體中IAT表資訊并比較函式地址,不同則說明有 IAT HOOK;

3.對于InLine HOOK,讀取行程代碼段中函式區域的跳轉指令運算元,若發現跳轉到別的函式區域,則說明有InLine HOOK;

4.使用工具模擬這個程序:選中行程和PE檔案,再點擊 IAT HOOK按鈕或者InLine HOOK按鈕就可進行檢測,

多開檢測和動態調式工具檢測

1.現在的打金作業室可以同時開多個號來刷金幣,那么需要限制游戲多開,當動態調式行程時,在任務管理器中可以看到調式工具的主執行緒成了調式行程,那么調式器記憶體里面必然會有調式行程的記憶體資訊,那么找到調式工具后再查找里面是否含有游戲行程的記憶體資訊,若有則可以判斷,游戲處于調式狀態,

2.對于防多開,經常會用到下面的代碼,那么外掛制作者修改跳轉指令或者NOP掉匯編代碼就可以實作多開,為了防止這種情況,我這里把CreateMutex函式頭部修改為CC,使用調式器的方式不斷監控例外事件,當CreateMutex函式頭部觸發中斷例外時,獲取互斥體變數名(地址為:ESP+8),并判斷該變數名是否出現過,若出現過,則檢測出行程出現多開,實作的關鍵代碼如下:

//創建互斥體
    HANDLE hMutex = CreateMutex(NULL,FALSE,"XXXXXX");
    DWORD dret = GetLastError();
    if(hMutex)
    {
        if (ERROR_ALREADY_EXISTS == dret)
        {
            CloseHandle(hMutex);
            return 0;
        }
    }
   .............
    //WaitForSingleObject(hMutex,INFINITE);
  ............
    ReleaseMutex(hMutex);
 
    return 0;
}

檢測多開的關鍵代碼:

//解除HOOK 恢復原值
        WriteProcessMemory(&pde->dwProcessId, g_pCreateMutex,&g_Orignal,sizeof(BYTE),NULL);
        //獲取執行緒背景關系
        ctx.ContextFlags = CONTEXT_CONTROL;
        GetThreadContext(g_cpdi2.hThread,&ctx);
        //得到ESP+8的值
        ReadProcessMemory(g_cpdi2.hProcess, LPVOID(ctx.Esp + 0x8), &EspContent, sizeof(DWORD), NULL);
        printf("EspContent: %x\n", EspContent);
        //取出地址并獲取字串
        Name[NameNum++] = (char*)(EspContent);
        //判斷有無重復互斥名
        list<string> list1;
        for (int i = 0; i < NameNum; i++)
        {
            list1.push_back(Name[i]);
        }
        int len1 = list1.size();
        list1.unique();
        int len2 = list1.size();
        //長度不等,則有重復元素
        if (len1 != len2)
        {
            IsOpenMore = 1;
            return -1;
        }
        //讓EIP為當前地址
        ctx.Eip = (DWORD)g_pCreateMutex;
        //設定執行緒背景關系
        SetThreadContext(g_cpdi2.hThread, &ctx);
        //繼續調式
        ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);

3.對于多開檢測,我還想出了三種檢測方法:

1.獲取行程代碼段資訊,使用代碼段資訊匹配的方式來判斷是否有相同的行程運行;

2.獲取行程所有的視窗句柄,匹配查找有無跟視窗標題相同的視窗,有則說明多開;

3.對行程名進行匹配;

4.對于調式工具檢測,還是使用代碼段識別法:獲取OD工具中代碼段資訊匹配尋找相同行程,若有,則再識別內部有無游戲行程的記憶體資訊并且再判斷調式行程是否為調式工具的子行程,兩者有一成立,則說明游戲行程處于調式狀態,實作的關鍵代碼如下:

void ToolAndOpenMore::GetToolCode(BYTE* ToolCode, CString strPath)
{
    //把PE檔案加載到記憶體中
    PVOID PeBuffer =PEFileToMemory(CStringToCharSz(strPath));
    //PE檔案頭
    pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)PeBuffer + pDosHeader->e_lfanew + 0x4);
    //節表數量
    DWORD SectionNum = pPEHeader->NumberOfSections;
    //PE可選頭
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
    DWORD EP = pOptionHeader->AddressOfEntryPoint;
    //代碼段節表頭
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
    tpSectionHeader = pSectionHeader;
    int j = 0;
    //遍歷OEP之后的代碼段資料
    for (int i = tpSectionHeader->PointerToRawData; i < tpSectionHeader->PointerToRawData + tpSectionHeader->SizeOfRawData && j<SZLEN; i++)
    {
        if (DataHs.FoaToRva(PeBuffer, i) < EP)
            continue;
        ToolCode[j++] = *((BYTE*)((DWORD)PeBuffer + i));
    }
    return;
}

5.接下來,使用工具進行檢測:多開檢測只需選中行程點擊“多開檢測”按鈕即可;調式工具檢測要選中行程和調式工具,操作情況如下所示:

添加反調式法

1.在游戲的PE檔案中添加反除錯,不為是一種較好的反外掛方法,在PE檔案中修改TLS表資訊,擴大最后一個節表并在空白區域添加PIMAGE_TLS_DIRECTORY結構資訊、TLS回呼函式,并在TLS回呼函式中添加反除錯功能即可實作反除錯,

2.實作反除錯的思路為:

1.把PE檔案加載到記憶體中,使用偏移的方式定位到TLS資料目錄,修改其中的 RVA欄位值和Size欄位值,讓 RVA指向PIMAGE_TLS_DIRECTORY起始地址;

2.定位到最后的節表結構并增加SizeOfRawData的值和修改Characteristics的值,使最后的節表具有可執行特性,

3.最后把生成的PIMAGE_TLS_DIRECTORY結構資料和回呼函式ShellCode復制到最后的節表中,

3.除了往PE檔案添加TLS反調式外,還可以添加SEH反除錯,實作的思路為:

1.先在代碼段,尋找足夠大的空閑區域,若沒有找到,則新增最后節表大小;

2.確定空閑區域開始地址,修改OEP的值,在空閑區域添加觸發SEH例外的Shellcode,在該Shellcode中有例外處理函式,該例外處理函式會判斷游戲是否處于調式中,如果沒有處于調式狀態則跳回OEP中,否則中止;

4.接下來使用工具添加反除錯,選中檔案,點擊按鈕即可,

以上使用的工具和原始碼可在該處下載:https://github.com/chenpan01/Anti_GameAssist,希望該工具及原始碼對大家有所幫助,工具存在bug的話,歡迎提出,大家共同探討,共同提升!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/297586.html

標籤:python

上一篇:Python資料結構與演算法(15)---希爾排序

下一篇:如何用str.format()批量生成網址【以豆瓣讀書為例】

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more