遊戲外掛原理解析與制作 - [內存數值修改類 篇二]
本章旨在講解如何利用高級語言根據變量數值尋找內存地址。涉及代碼以C#為例。
我用C#寫了一個WinForm形式的Demo,界面如下:
源代碼:
//血量初始值 private int value = 1000; public Form1() { InitializeComponent(); } /// <summary> /// 刷新界面:將最新的血量顯示在界面 /// </summary> /// <param name="sender"></param>/// <param name="e"></param> private void btn_refresh_Click(object sender, EventArgs e) { this.label_display.Text = value.ToString(); } /// <summary> /// 更新血量:將自定義的數值寫入血量變量 /// </summary> /// <param name="sender"></param>/// <param name="e"></param> private void btn_update_Click(object sender, EventArgs e) { int iVaule = -1; bool ParseResult = int.TryParse(this.textBox_value.Text,out iVaule); if (ParseResult) { value = iVaule;this.label_display.Text = this.textBox_value.Text; } }
很簡單的一個Demo:一個名為value的變量,整數型,賦予其初始值1000;兩個按鈕:修改按鈕點擊後把文本框的數值賦值給value,並且修改標簽文本=修改後value的值;另一個刷新按鈕,點下後更新標簽文本=value的最新值。
回顧上一章節,我們講到查詢數值的內存地址需要用到兩個函數VirtualQueryEx和ReadProcessMemory:
其中VirtualQueryEx的第三個參數是一個用於接收內存信息的結構體指針,來看一下組成這個結構體的成員
//接收內存信息的結構體 public struct MEMORY_BASIC_INFORMATION { //區域基地址 public int BaseAddress; //分配基地址 public int AllocationBase; //區域被初次保留時賦予的保護屬性 public int AllocationProtect; //區域大小 public int RegionSize; //狀態 public int State; //保護屬性 public int Protect; //類型 public int lType; }
這些註釋是我從百度百科上摘抄的,更準確的解釋建議查閱MSDN的API,下面貼代碼的時候我也會據我的理解去解釋涉及到的成員,但還是建議深入學習計算機的操作系統原理才能真正掌握這些術語與它們的意義。特地貼出這一段是我認為這是整個外掛制作過程中最重要的一個步驟。套用我們現成的模板或者利用後續我也會提到的一系列輔助工具去完成外掛的制作,是很難成長的,一些知名的遊戲靠工具搜索基址千難萬難,只有慢慢的去理解這些API、了解寄存器和匯編語言,才能走的更遠。
原歸正傳,我就直接跟著實際測試來一步一步講解:
1). 打開測試程序
程序的名稱:WinMemory_Test
2). 根據上一章節通過進程名稱獲取PID=7956
3). 還是上一章節提到的通過PID=7956獲取進程句柄Handle=1072
4). 通過Handle循環遍歷可讀寫內存地址,取得字節數組。
public void SearchAddress() { MEMORY_BASIC_INFORMATION MBInfo = new MEMORY_BASIC_INFORMATION(); //獲取結構體大小[單次讀取字節數] int MBSize = Marshal.SizeOf(MBInfo); //從0x00開始查詢 StartAddress = 0x000000; //實際讀取的字節數 int ReadSize = 0; //從0開始查詢,直到查詢到整形的最大值2147483647 while (StartAddress >= 0 && StartAddress <= 0x7fffffff && MBInfo.RegionSize >= 0) { //讀取結果存入輸出參數MBInfo MBSize = VirtualQueryEx(hProcess, (IntPtr)StartAddress, out MBInfo, Marshal.SizeOf(MBInfo)); //如果實際讀取到的字節數等於結構體MEMORY_BASIC_INFORMATION字節數,表示讀取成功 if (MBSize == Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION))) { //PAGE_READWRITE:允許讀寫的內存區。 //MEM_COMMIT:已分配物理內存[要找的數值確定了,那麽內存肯定提前分配了]。 if (MBInfo.Protect == PAGE_READWRITE && MBInfo.State == MEM_COMMIT) { byte[] FindArray = new byte[MBInfo.RegionSize]; //把讀取到的字節寫入上面定義的數組byData中 if (ReadProcessMemory(hProcess, (IntPtr)StartAddress, FindArray, MBInfo.RegionSize, out ReadSize)) //如果讀取的字節數無誤 if (ReadSize == MBInfo.RegionSize) { //處理數據[對比分析] DealData(DataArray, StartAddress); } } } else { break; } StartAddress += MBInfo.RegionSize; } }
5). 將獲取的字節數組轉化整型與1000進行對比,將尋找到的所有結果保存到全局List.
public void DealData(byte[] DataArray, int StartAddress) { byte[] intBuff = new byte[4]; for (int i = 0; i < DataArray.Length - 4; i++) { Array.Copy(DataArray, i, intBuff, 0, 4); int num = BitConverter.ToInt32(intBuff, 0); if (num == 1000) { AddressList.Add(StartAddress + i); } } }
看一下結果:
至此,Demo中整形數值等於1000的地址已經全部被我們找到了,下一章節講解如何定位我們所要查找的那個“1000”以及修改其值。
PS:轉載請附帶原文路徑:http://www.cnblogs.com/lene-y/p/7107526.html ,我已委托“維權騎士”為我的文章進行維權行動。
歡迎關註微信公眾號[遊戲外掛原理解析與制作],對本文有不理解的地方或者不同的觀點可以給我留言,一定回復。
遊戲外掛原理解析與制作 - [內存數值修改類 篇二]