游戲外掛原理解析與製作 - [記憶體數值修改類 篇一]

来源:http://www.cnblogs.com/lene-y/archive/2017/06/30/7096485.html
-Advertisement-
Play Games

本章旨在講解外掛實現原理,未深入涉及至代碼層面。希望能與對這方面感興趣的朋友多多交流,畢竟理論是死的,套路是固定的,只有破解經驗是花大量時間和心血積累的。 對於單機游戲而言,游戲中絕大部分的參數(比如血、藍、能量亦或是金幣)都存儲在電腦的堆棧中,一些類似劇情進度的則加密後寫入本地的自定義配置文件中 ...


  本章旨在講解外掛實現原理,未深入涉及至代碼層面。希望能與對這方面感興趣的朋友多多交流,畢竟理論是死的,套路是固定的,只有破解經驗是花大量時間和心血積累的。

  • 對於單機游戲而言,游戲中絕大部分的參數(比如血、藍、能量亦或是金幣)都存儲在電腦的堆棧中,一些類似劇情進度的則加密後寫入本地的自定義配置文件中;
  • 對於頁游、網游和手游,雖然伺服器保存了大量的重要的參數,但由於客戶端不可避免的需要進行大量的計算和資源的載入,本地記憶體種必定存有部分的臨時變數,通過判斷這些變數的變化規律和函數的破密尋到利於自身的參數,比如傷害值一類,繼而尋找該變數的記憶體地址,根據指針偏移分析獲得記憶體基址,再提升許可權利用Windows API把自定義數值寫入該記憶體塊,就完成了修改某項數值的操作,一般來說,只要破解了一項數值,利用規律繼而破解其他數值就更加容易了。

    一般套路就是上述,一些防護性強大的游戲會在上述的每一步中都設置難題,等著我們去破解。

 

    在此之前,我們來瞭解一些基礎知識:

  • 數據類型:游戲中的血量、藍、生命值,我們將他們稱之為變數,變數包含了變數名稱和數據類型,那麼它的名字就是"血量、生命值"等等,而它的數據類型決定了數值以何種方式存儲到電腦的記憶體中,想要找到自身需要的變數,如果可以確定這個變數的數據類型或者有根據的推測來縮小變數的數據類型範圍,那麼對於快速定位該變數是具有幫助性的。以下是幾種常見的變數類型:

   整數型:游戲中比如血量、法力可能用到這種類型。

   位元組型:根據不同的編輯器,1個整形占用N個位元組(N>1),一般很早之前出產的GBA游戲為了節省開銷會用到這種類型。

   浮點型:帶有小數點的數字,如果金幣或者傷害值帶有小數點,那很可能是這種數據類型保存的。

   文本型:比如世界喊話,人物命名,一般採用文本型保存這類數據。

   推薦閱讀《數據結構》相關書籍更好的熟悉數據結構,有編程經驗的就不必多說了。

 

  • 進程:每一個應用程式/游戲啟動,都會產生至少一個進程Process,在任務管理器里可以看到進程名稱和進程PID

   

 

  • 句柄:英文HANDLE,一個整數值。數據的地址需要變動,變動以後就需要有人來記錄管理變動,就好像戶籍管理一樣,因此系統用句柄來記載數據地址的變更。肉眼看到的一個個文件夾,視窗,按鈕,圖標,應用程式能夠通過句柄訪問相應的對象的信息

  推薦閱讀《電腦操作系統》相關書籍更多的瞭解電腦原理。

  

  進入正題,根據目的反向推導下需要得到哪些信息,我們最終才能想要實現修改數值。

  修改變數的數值→得先找到存儲變數的記憶體地址→得先找到游戲視窗的句柄→得先找到游戲的進程。

  根據什麼依據推導出的路線呢?

 

  Windows系統庫的kernel32.dll庫文件中包含了記憶體操作的API,其中VirtualQueryEx用於查詢地址空間中記憶體地址的信息。

   函數原型:

  /// <summary>
  /// 查詢地址空間中記憶體地址存儲的信息
  /// </summary>
  /// <param name="hProcess">句柄</param>
  /// <param name="lpAddress">記憶體地址</param>
  /// <param name="lpBuffer">結構體指針</param>
  /// <param name="dwLength">結構體大小</param>
  /// <returns>寫入位元組數</returns>
  [DllImport("kernel32.dll")]     
      public static extern int VirtualQueryEx(
      IntPtr hProcess, 
      IntPtr lpAddress, 
      out MEMORY_BASIC_INFORMATION lpBuffer, 
      int dwLength);

  我們下麵逐個分析參數:

  hProcess:顧名思義,進程句柄,也就是說想要查詢地址存放的信息,首先得獲得進程的句柄。

  lpAddress:查詢的記憶體地址,輸入參數,需要主動提供地址。但我們並不知道我們需要的數值它被存放的地址,我們只能一個頁面一個頁面的猜測,直到掃描到某個頁面的某塊記憶體裡面存放的信息正是與我們需要的信息一致或是存在一定的函數關係。其次,對於同一個數值也許出現在多個地方,比如 在某個時間區間人物的攻擊數值和防禦數值等同都為1200,那麼說明至少有兩個地址存放了這個數據。我們需要把這些地址全都篩選出來。

  lpBuffer:結構體指針,用於存放記憶體信息。

  dwLength:上述結構體的大小。

  返回值:函數寫入lpBuffer的位元組數,如果位元組數等於結構體PMEMORY_BASIC_INFORMATION的大小,表示函數執行成功。

 

  但此函數只負責獲取記憶體信息,而查詢記憶體信息中具體存放數值則用到另一函數ReadProcessMemory,來看一下函數原型:

      /// <summary>
      /// 根據進程句柄讀入該進程的某個記憶體空間
      /// </summary>
      /// <param name="lpProcess">進程句柄</param>
      /// <param name="lpBaseAddress">記憶體讀取的起始地址</param>
      /// <param name="lpBuffer">寫入地址</param>
      /// <param name="nSize">寫入位元組數</param>
      /// <param name="BytesRead">實際傳遞的位元組數</param>
      /// <returns>讀取結果</returns>
      [DllImportAttribute("kernel32.dll", EntryPoint = "ReadProcessMemory")]
      public static extern bool ReadProcessMemory
      (
          IntPtr lpProcess,
          IntPtr lpBaseAddress,
          IntPtr lpBuffer,
          int nSize,
          IntPtr BytesRead
      );    

 

  此函數將根據句柄讀取該進程的某個記憶體空間,並將讀取到的位元組數寫入到我們開闢的一塊空間中,而此空間存放的正是我們苦苦追尋的“有意義的數值”。此函數的部分參數依賴於上一個函數VirtualQueryEx產生的結果。

 

  根據上面的API,先來看怎麼獲取第一個參數:窗體句柄,同樣的kernel32.dll提供了名為OpenProcess的函數用來打開一個已存在的進程對象,並返回進程的句柄。

  函數原型:

  /// <summary>
  /// 打開一個已存在的進程對象,並返回進程的句柄
  /// </summary>
  /// <param name="iAccess">渴望得到的訪問許可權</param>
  /// <param name="Handle">是否繼承句柄</param>
  /// <param name="ProcessID">進程PID</param>
  /// <returns>進程的句柄</returns>
  [DllImportAttribute("kernel32.dll", EntryPoint = "OpenProcess")]
  public static extern IntPtr OpenProcess
  (
      int iAccess,
      bool Handle,
      int ProcessID
  );

  dwDesiredAccess :渴望得到的訪問許可權,這裡預設填寫PROCESS_ALL_ACCESS | 0x1F0FFF 給予所有可能允許的許可權。

  bInheritHandle :是否繼承句柄,FALSE即可。

  dwProcessId :進程標示符PID。

 

  對於進程PID各種編程語言有自己的獲取方式,以C#語言為例(針對非多開客戶端):

  //根據進程名稱獲取PID
  public int GetPIDByPName(string ProcessName)
      {
          Process[] ArrayProcess = Process.GetProcessesByName(processName);
        foreach (Process pro in ArrayProcess)
        {
            return pro.Id;
        }
        return -1;
    }

  我們獲取到進程的PID以後,就可以調用OpenProcess獲取窗體的句柄,然後利用函數VirtualQueryEx遍歷記憶體查找地址信息,根據地址利用ReadProcessMemory查找具體存放的值,最後利用WriteProcessMemory把修改後的值寫入該地址,這樣就完成了一次數據的修改。來看一下API的函數原型:

  /// <summary>
  /// 寫入某一進程的記憶體區域
  /// </summary>
  /// <param name="lpProcess">進程句柄</param>
  /// <param name="lpBaseAddress">寫入的記憶體首地址</param>
  /// <param name="lpBuffer">寫入數據的指針</param>
  /// <param name="nSize">寫入位元組數</param>
  /// <param name="BytesWrite">實際寫入位元組數的指針</param>
  /// <returns>大於0代表成功</returns>
  [DllImportAttribute("kernel32.dll", EntryPoint = "WriteProcessMemory")]
  public static extern bool WriteProcessMemory
      (
          IntPtr lpProcess,
          IntPtr lpBaseAddress,
          int [] lpBuffer,
          int nSize,
          IntPtr BytesWrite
      );

  參數就不再一一解釋了,註釋都有,最後一個參數預設填寫Null或者IntPtr.ZeroI即可。

  到了這裡,修改游戲數值的原理和套路已經很明白了,甚至脫離游戲來講,任何的應用如果沒有對緩存中的數據進行良好的加密,都是存在很大的風險隱患的,這一章節主要瞭解一些常用到的名詞和API的運用。具體如何利用代碼進行調用API,以及更詳細的剖析每一步的邏輯,將在下一章節講述。

  PS:轉載請附帶原文路徑:http://www.cnblogs.com/lene-y/p/7096485.html ,我已委托“維權騎士”為我的文章進行維權行動。

  歡迎關註微信公眾號[游戲外掛原理解析與製作],對本文有不理解的地方或者不同的觀點可以給我留言,一定回覆。

       


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • inner join : linq 預設使用Inner Join的鏈接方式,如下麵的表達式一樣: Left Join: 左鏈接返回左表的全部數據,以及右表中滿足鏈接條件和不滿足鏈接條件的數據,不滿足的取欄位值, 在一對多的的情況下主表的數據會被重覆, 左外鏈接的實現方式如下,註意這裡面和上面的寫法不 ...
  • Asp.Net Core App為了做到跨平臺,自帶了一個輕量級的Web Server - Kestrel,那麼要在IIS中部署Asp.Net Core App,就必須有一種新的機制來協調IIS與Kestrel Server之間的數據傳遞。 ...
  • 鑒於之前學習的是Java,學習Java的時候一直使用的是近乎神級的開發軟體IDEA,出於對IDEA的崇拜,轉到C#大陣營後,瞭解到JetBrains公司也有相應的Vs插件, 就果斷安裝了Resharper(代碼生成工具), 在此總結了Vs2015中安裝ReSharper的步驟: 1.首先必須安裝Vi ...
  • 現在有一個發佈好的WebService,地址是http://hovertree.com:2706/UploadExpenseToConstract.asmx能不能把最後面的asmx去掉呢?變成http://hovertree.com:2706/UploadExpenseToConstract但效果和 ...
  • 前言 前幾天在博客園看到有園友在分享關於微軟的一個微服務架構的示常式序,想必大家都已經知道了,那就是 "eShopOnContainers" 。 我們先不看項目的尾碼名稱 OnXXX ,因為除了 OnContainers 還有 OnAzure,OnWeb,OnKubernetes 以及 OnServ ...
  • 一、請求從路由開始 1.為什麼需要路由? (1).屏蔽物理路徑、提高安全性 (2).有利於搜索引擎優化 2.定義路由的規則 (1).基於模式匹配的路由規則 語法:{占位符1}字面量1{占位符2}字面量2...{占位符n}...字面量n 註:字面量可能是一個固定的字元,比較常見的是"/",也可以是一個 ...
  • 物性路由:將路由和控制器放在一起,這樣更簡單方便,還可以處理複雜的路由場景 傳統路由:集中、強制、基於代碼風格來定義的。 每個MVC應用程式都需要路由來定義自己的處理請求方式,路由是MVC是應用程式的入口點。我們先瞭解一下路由涉及的主要概念:路由定義是從URL模板開始的,因為它指定了與路由相匹配的模 ...
  • 百度翻譯是什麼,可以吃嗎?相信很多人都熟悉,它是我們生活中必不可少的一隻東東。 但是,百度翻譯開發平臺只有每月只能翻譯200萬個字元,多出的要按照49.00/百萬字元來算。對於我醬紫的乞丐程式員來說,其實已經是足足的了。 接下來進入正題,分為2個部分講,一是免費的百度翻譯,二是收費的百度翻譯(此處應 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...