【外掛修改器簡單介紹】
游戲外掛分為很多種類型,例如本地客戶端的內存修改、遠程服務器的封包破解。
一般的網游由於服務器的機能限制,並不會將游戲產生的所有數據、計算都提供給服務器來承擔。經常的,服務器只會將玩家人物的屬性、血量、加點、金錢、裝備等重要信息儲存在服務器本地,計算結果和數據傳遞則是通過加密封包來和客戶端傳輸。
因此網游如果封包被破解而被用戶發送偽造的封包數據將造成非常嚴重的后果。
【C#本地修改器】
如上所述,雖然封包的一般無法破解,但是利用服務器無法儲存所有的信息這個特點,我們可以通過修改本地客戶端的內存數據來制作一些建議的外掛,如修改坐標達到穿牆、加速的效果。
修改這類的本地數據,一般我們使用到 Cheatgine 來搜索內存特定數值。關於這個這工具網上已經有很多完善的教程,這邊就不贅述了。
這邊通過QQ連連看的示例來教程一般的C#的外掛制作。
通過Cheatgine搜索到時間、羅盤、重列的數量、本機座位的內存基址偏移量,再繪制出界面就可以開始制作。

(CheatEngine)

(簡易的界面UI)
這邊提供一個C#的內存修改讀取抽象類
1 public abstract class Helper //內存讀寫核心
2 {
3 [DllImportAttribute("kernel32.dll", EntryPoint = "ReadProcessMemory")]
4 public static extern bool ReadProcessMemory
5 (
6 IntPtr hProcess,
7 IntPtr lpBaseAddress,
8 IntPtr lpBuffer,
9 int nSize,
10 IntPtr lpNumberOfBytesRead
11 );
12
13 [DllImportAttribute("kernel32.dll", EntryPoint = "OpenProcess")]
14 public static extern IntPtr OpenProcess
15 (
16 int dwDesiredAccess,
17 bool bInheritHandle,
18 int dwProcessId
19 );
20
21 [DllImport("kernel32.dll")]
22 private static extern void CloseHandle
23 (
24 IntPtr hObject
25 );
26
27 //寫內存
28 [DllImportAttribute("kernel32.dll", EntryPoint = "WriteProcessMemory")]
29 public static extern bool WriteProcessMemory
30 (
31 IntPtr hProcess,
32 IntPtr lpBaseAddress,
33 int[] lpBuffer,
34 int nSize,
35 IntPtr lpNumberOfBytesWritten
36 );
37
38 //獲取窗體的進程標識ID
39 public static int GetPid(string windowTitle)
40 {
41 int rs = 0;
42 Process[] arrayProcess = Process.GetProcesses();
43 foreach (Process p in arrayProcess)
44 {
45 if (p.MainWindowTitle.IndexOf(windowTitle) != -1)
46 {
47 rs = p.Id;
48 break;
49 }
50 }
51
52 return rs;
53 }
54
55 //根據進程名獲取PID
56 public static int GetPidByProcessName(string processName)
57 {
58 Process[] arrayProcess = Process.GetProcessesByName(processName);
59
60 foreach (Process p in arrayProcess)
61 {
62 return p.Id;
63 }
64 return 0;
65 }
66
67 //根據窗體標題查找窗口句柄(支持模糊匹配)
68 public static IntPtr FindWindow(string title)
69 {
70 Process[] ps = Process.GetProcesses();
71 foreach (Process p in ps)
72 {
73 if (p.MainWindowTitle.IndexOf(title) != -1)
74 {
75 return p.MainWindowHandle;
76 }
77 }
78 return IntPtr.Zero;
79 }
80
81 //讀取內存中的值
82 public static int ReadMemoryValue(int baseAddress, string processName)
83 {
84 try
85 {
86 byte[] buffer = new byte[4];
87 IntPtr byteAddress = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0); //獲取緩沖區地址
88 IntPtr hProcess = OpenProcess(0x1F0FFF, false, GetPid(processName));
89 ReadProcessMemory(hProcess, (IntPtr)baseAddress, byteAddress, 4, IntPtr.Zero); //將制定內存中的值讀入緩沖區
90 CloseHandle(hProcess);
91 return Marshal.ReadInt32(byteAddress);
92 }
93 catch
94 {
95 return 0;
96 }
97 }
98
99 //將值寫入指定內存地址中
100 public static void WriteMemoryValue(int baseAddress, string processName, int value)
101 {
102 IntPtr hProcess = OpenProcess(0x1F0FFF, false, GetPid(processName)); //0x1F0FFF 最高權限
103 WriteProcessMemory(hProcess, (IntPtr)baseAddress, new int[] { value }, 4, IntPtr.Zero);
104 CloseHandle(hProcess);
105 }
106
107 }
將找到的內存基址定義好
1 //將值寫入指定內存中
2 public void WriteMemory(int baseAdd, int value)
3 {
4 Helper.WriteMemoryValue(baseAdd, processName, value);
5 }
6
7 private int baseAddress = 0x00111DEC; //游戲內存基址
8 private string processName = "QQ游戲 - 連連看角色版"; //游戲進程名字
9
10 int temp = 0; int sit = 0;
11
利用所提供的內存讀寫抽象類寫入內存,並利用一個timer持續寫入這個內存數值以達到鎖定數值的目的。
1 private void timer1_Tick(object sender, EventArgs e) //凍結時間
2 {
3 int address = ReadMemoryValue(baseAddress);
4 address += 0x49d8;
5
6 if (checkBox1.Checked == true)
7 {
8 timer1.Enabled = true;
9 WriteMemory(address, 700);
10 label1.Text = "已凍結";
11
12 }
13
14 if (checkBox1.Checked != true) label1.Text = "未凍結";
15 }
以上就是一個例子,一般的單機游戲修改器也可以用這樣的方法制作完成。
當然現在有不少網游有保護盾,可以防止游戲客戶端內存被修改,這類的技術一般是用驅動、內核、進程掛鈎完成,可以利用XT手動解除這些保護。
