1. 程式人生 > >如何打造一款遊戲外掛

如何打造一款遊戲外掛

目錄

  • 前言
  • 1.記憶體資料排查
    • 1.1 例-植物大戰殭屍
    • 1.2 例-只狼
  • 2.外掛程式編寫
  • 3.掛機類自動外掛
  • 4.網路遊戲外掛

前言

記得小學的時候玩頁遊把家裡電腦弄壞了(怎麼回事,頁遊還能弄壞電腦?),然後我爸拿去重新裝了系統,回來之後電腦裡有了兩個新的單機遊戲,植物大戰殭屍和大魚吃小魚。

那個年代周圍的小學生都是賽爾號/奧比島/洛克王國/天書奇譚/龍之刃/夢幻西遊,玩摩爾莊園的小學生說出來都不是九年義務教育的。植物大戰殭屍雖然現在看來比較久遠,但他的遊戲元素卻是推生出許多遊戲音樂文化,比如Billie Eilish的《bad guy》,節奏簡直就是植物大戰殭屍的燈 燈 燈 燈 燈燈~了。

當時因為生存模式被虐,在網上找了修改器,可以修改陽光點數,那時候雖然沒學計算機專業,但對修改器的執行機制還是有所猜測的,當時小學因為不知道記憶體這回事,所以是覺得修改的檔案中的資料。

這個猜測如今看起來很搞笑,但也不失道理(要開始狡辯了),有些遊戲的資料可能是先放在檔案中的,在遊戲啟動之後把檔案中的資料讀取到遊戲的記憶體區域,所以我們需要先修改資料所在的檔案內容,然後啟動遊戲,但是很多遊戲的資料檔案是加密的(出大問題,騎馬與砍殺就沒有加密),我們沒辦法從檔案進行修改,所以需要讓遊戲程序先把它讀到記憶體,然後從記憶體修改,然後再退出遊戲的時候遊戲程序自動把它儲存下來,之後也就是這個資料了。

所以綜上可以發現有這幾類修改物件:

  1. 遊戲資料在本地未加密檔案,直接修改它來修改遊戲資料

    • 這一類的代表就是騎馬與砍殺,它的角色資料在文件中,可以肆意修改玩家屬性,但是不要修改太大,因為資料會溢位。
      此外它的遊戲資料,如NPC對話,NPC姓名,國家名稱,城堡名稱都是明文給出的(還是用Excel儲存的???),最可怕的是它的內部機制,如兵種上限/士氣/圍攻時間/酒館花費等都是明文的,怪不得騎砍會有各種版本。
  2. 資料封裝,通過修改記憶體來修改遊戲資料

  3. 網路遊戲,資料在對方伺服器,無法直接修改,通過模擬網路請求表單來模擬自己獲得遊戲物品(看對方伺服器邏輯嚴密程度了)

    • 舉幾個例子(騰訊遊戲全部忽略,這個不在新手教程裡面),你正在玩一個網頁遊戲龍之刃,打死了一個小怪,獲得了15銀幣,這時你的網頁需要給伺服器傳送給你銀幣資料+15的資訊,你可以用瀏覽器自帶的工具開發者除錯看看這個資料的格式,然後模仿一個相似的,傳送的伺服器,比較菜的遊戲廠商當然無法完善這方面的邏輯了。(另外我有一個想法,steam在我們玩遊戲的時候應該會有一個執行緒監控我們正在玩什麼遊戲,所以我們可以監控這個執行緒發出的網路表單,然後我們自己不斷髮出,這不是卡套刷滿?)

下面開始實戰部分,因為不想再下載騎砍,而且這種修改檔案的教程在騎砍官網很多,所以我就跳過啦。

一. 記憶體資料排查

遊戲作為程序執行在我們的電腦中,所以要修改它的資料我們可以在記憶體進行搜尋修改。

拿植物大戰殭屍為例,我們要修改的是他的陽光數量。假如我們現在的陽光值是75,我們需要在記憶體中查詢75的數字。

先用記憶體工具開啟植物大戰殭屍的程序。

然後查詢25這個數字,下圖只找到一個,所以陽光的資料就儲存在21BF10C8這個地址了,但很多遊戲搜尋出來可能不止這一個地址,我們需要將查詢的那個道具(陽光)花費掉一點,變成另一個值再次掃描一次才能精準定位。

修改這個地址的數值。

在遊戲中檢視陽光數量。

是不是太簡單了?! 這是因為這個遊戲本身設計已經和這個時代脫軌了,如今的遊戲製造商不僅要考慮內容,還需要在安全上和高玩鬥智鬥勇。

接下來再拿一個遊戲練練手,就是最近非常火的宮崎英高老賊作品只狼。

Sekiro : Shadows Die Twice

作為一個九周目下忍,這款遊戲真的讓我知道了死字怎麼寫。

先來看看我當前的金幣,2323。

然後按照慣例選擇只狼的程序,搜尋2323這個資料,可以發現出現了很多。

我去殺了一個小怪,把金幣加到了2360。

再次搜尋發現還有5個地址存在,直接修改會發現不會改變,有一種猜測是它用了一種類似於病毒中多程序相互守護一樣的方法,會讓資料保持一致,所以我們可以直接全部選中同事修改,會發現第三個沒有修改成功,因為這個值不是揹包中金幣數量,而是戰鬥介面的數量,你可以自己試試。

金幣順利添加了兩萬,其它資料在本地的都是如此,可以自己嘗試,但這種方法效率總是不高,我們可以使用程式碼的形式來把這個修改過程自動化。

這裡多說一句,程式碼的方式是把資料的地址編碼到程式碼中,如果遊戲的更新導致這個地址改變話,程式碼裡面也要同步修改,所以需要頻繁更新的遊戲其實修改器很多會失效。

二. 外掛程式編寫

先來了解幾個Windows的API。

HWND FindWindow(LPCTSTR IpClassName,LPCTSTR IpWindowName); 
通過類名或視窗名查詢,返回視窗控制代碼
DWORD GetWindowThreadProcessId(HWND hWnd,LPDWORD lpdwProcessId); 
得到視窗控制代碼後通過GetWindowThreadProcessId這個函式來獲得視窗所屬程序ID和執行緒ID
HANDLE OpenProcess(DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwProcessId) 
開啟一個已存在的程序物件,並返回程序的控制代碼
bool WriteProcessMemory(HANDLE hProcess,LPVOID lpBaseAddress,LPVOID lpBuffer,DWORD nSize,LPDWORD lpNumberOfBytesWritten ); 
能寫入某一程序的記憶體區域。入口區必須可以訪問,否則操作將失敗

附上程式碼:

#include <windows.h>
#include <stdio.h>
int main() {
    HWND h = ::FindWindow(NULL, "植物大戰殭屍中文版");   //  尋找並開啟程序
    DWORD processid;
    GetWindowThreadProcessId(h, &processid);
    HANDLE hprocess = 0;
    hprocess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processid);
    if (hprocess == 0) {    //  對應處理
        printf("開啟程序失敗!\n");
        return 1;
    } else {
        printf("開啟程序成功!\n");
        DWORD hp = 3000;    //  要修改的遊戲資料最大值 
        LPCVOID addr = (LPVOID)0x21BF10C8;  //  通過CE找到的遊戲資料地址
        DWORD res = WriteProcessMemory(hprocess, (LPVOID)addr, &hp, 4, 0);  //  寫入記憶體修改遊戲資料
        return 0;
    }
}

這樣一個程式碼,可以讓我們隨時呼叫,把陽光修改為2000,這個程式沒有做圖形化介面,因為太簡單了,所以當做入門Demo吧。

我們修改了通過地址直接遊戲的資料,那麼能不能說做一個遊戲助手,讀出遊戲的某些資料來輔助玩家呢? CE工具雖然可以搜尋讀取,但是效率太差,接下來介紹一個新的API,來讀取固定地址的資料。

HWND ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); 
根據程序控制代碼讀入該程序的某個記憶體空間lpBaseAddress的nSize位元組,並寫入緩衝區lpBuffer,多次計算基址和偏移即可 

嘗試讀取 :

附上程式碼實現:

#include <stdio.h>
#include <windows.h>
int main() {
    HWND h = ::FindWindow(NULL, "植物大戰殭屍中文版");   //    尋找並開啟程序
    DWORD processid;
    GetWindowThreadProcessId(h, &processid);
    HANDLE processh = 0;
    processh = OpenProcess(PROCESS_ALL_ACCESS,FALSE,processid);

    if (processh == 0) {    //    對應處理
        printf("開啟程序失敗!\n");
        return 1;
    } else {
        printf("開啟程序成功!\n");
        int sun;    //    用於存放陽光資料
        LPCVOID mbase = (LPCVOID)0x1E0CF020;
        LPVOID mbuffer = (LPVOID)&sun;
        ::ReadProcessMemory(processh, mbase, mbuffer, 4,  0);
        printf("您有陽光:%d\n", sun);
    return 0;
}

三. 掛機自動外掛

上述都是一些修改資料的外掛,此外還有一種是自動操作類的修改器,比如自動玩連連看(霧),自動下棋(霧),自動拼圖(笑)。

例如下面這樣的連連看,當你用CE觀察記憶體時,會發現它的資料奇妙在於這是一個二維陣列,在記憶體中不同牌有不同數值,此處參考網上lgx大佬給出的方案。

想要自動消除連連看,肯定需要先模擬滑鼠的操作,介紹一個API,它可以模擬滑鼠操作,但不會移動我們的物理滑鼠(虛無點選?)。

LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);
其中Msg為WM_LBUTTONDOWN,WM_LBUTTONUP,表示對滑鼠的軟體模擬操作(雖然相容性和安全性不如硬體模擬的mouse_event,但不改變移動滑鼠指標)

除此之外,對一個二維陣列尋找可以消除的兩幅牌,可以使用BFS演算法實現。

void clearapair() { //找到可消去的兩個點並點選
     POINT p1, p2;
     int x1, y1, x2, y2;
      for (y1=0; y1<11; y1++)
          for (x1=0; x1<19; x1++) {
             if (!chessdata[y1][x1])
                continue;
              for (y2=0; y2<11; y2++)
                for (x2=0; x2<19; x2++)
                  if (chessdata[y2][x2] && (chessdata[y1][x1]==chessdata[y2][x2]) && (x1!=x2 || y1!=y2) )  {  
                      p1.x=x1; p1.y=y1;
                      p2.x=x2; p2.y=y2;
                      readchess();
                      if (llk_bfs(y1, x1, y2, x2) != -1) {
                        click2p(p1, p2);
                        return; 
                      }
                  }
          }
}

四. 網路遊戲修改

這個是我從小學四年級開始玩的遊戲,因為時間很長沒有登入,這裡累積了很多經驗,在領取之前我們可以先觀察一下。

抓到了一條表單。

初步分析,經驗資料不是直接明文規定的,而是從了其他數值代替,並且獲取經驗這一型別也是有其他數字代替,這裡初步猜測為age。

好吧,可能種類太多,抓不出來相同的Age。

但是我換了一種方式,對寵物使用了兩次經驗包,抓下來的兩次包,都是叫fcode1f3....的這個包。

其中Age型別也是一樣。

現在過了幾分鐘,我們把第三個包也使用掉。

囊達? 難道,這個Age真的是...(小聲bb)

還是不確定,讓我們用爬蟲來模擬這次表單吧,刷爆(封號)!!!

測試N次過後...

成功啦!!!

需要注意的是,這個遊戲伺服器以及幾年沒更新了,上一條公告還是十年前的。

如果你要使用爬蟲來模擬使用道具,非常關鍵的一個資訊就是cookie,這是確認你登入的賬號的判斷資訊。

下面貼出程式碼(我的cookie肯定要刪除掉啦)

import java.util.ArrayList;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

public class Renren {
    private static String renRenLoginURL = "http://resourcemop.l.imop.com/res/fc/fcde1f39034b164a0d5fe7e455b0c32e";
    private HttpResponse response;
    private CloseableHttpClient httpclient = HttpClients.createDefault();
    private boolean login() {
        HttpGet httpGet = new HttpGet(renRenLoginURL);
        httpGet.setHeader("Accept-Ranges", "bytes");
        httpGet.setHeader("Age", "74342");
        httpGet.setHeader("Cache-Control", "");
        httpGet.setHeader("Content-Length", "3266");
        httpGet.setHeader("Content-Type", "text/plain");
        httpGet.setHeader("Date", "Thu, 23 May 2019 08:41:58 GMT");
        httpGet.setHeader("Expires", "Sun, 20 May 2029 08:41:58 GMT");
        httpGet.setHeader("Last-Modified", "Wed, 22 May 2019 08:13:20 GMT");
        httpGet.setHeader("Server", "Apache");
        httpGet.setHeader("Via", "1.0 Static1.lzr.squid1:80 (squid/2.6.STABLE7)");
        httpGet.setHeader("X-Cache", "HIT from Static1.lzr.squid1");
        httpGet.setHeader("Referer", "http://s34.l.imop.com/");
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36");
        try {
            response = httpclient.execute(httpGet);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            httpGet.abort();
        }
        return true;
    }

    public static void main(String[] args) {
        Renren renRen = new Renren();
        renRen.login();
    }
}

這個其實是兩年前寫的人人網爬蟲,改了改表單資料就可以用啦。

篇幅有限,所以暫時就寫這麼多。之後可能會繼續更新其他的修改教程,steam掛卡這個我是一定要寫的。

敲程式碼是熱愛,敲到世界充滿愛! 撤退