C#本地修改器
- C#做外挂的常用API,本人用了很久,基本没发现问题
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Runtime.InteropServices; //这个肯定要的
- namespace WindowsApplication1
- {
- class win32API
- {
- public const int OPEN_PROCESS_ALL = 2035711;
- public const int PAGE_READWRITE = 4;
- public const int PROCESS_CREATE_THREAD = 2;
- public const int PROCESS_HEAP_ENTRY_BUSY = 4;
- public const int PROCESS_VM_OPERATION = 8;
- public const int PROCESS_VM_READ = 256;
- public const int PROCESS_VM_WRITE = 32;
- private const int PAGE_EXECUTE_READWRITE = 0x4;
- private const int MEM_COMMIT = 4096;
- private const int MEM_RELEASE = 0x8000;
- private const int MEM_DECOMMIT = 0x4000;
- private const int PROCESS_ALL_ACCESS = 0x1F0FFF;
- //查找窗体
- [DllImport("User32.dll", EntryPoint = "FindWindow")]
- public extern static IntPtr FindWindow(
- string lpClassName,
- string lpWindowName
- );
- //得到目标进程句柄的函数
- [DllImport("USER32.DLL")]
- public extern static int GetWindowThreadProcessId(
- int hwnd,
- ref int lpdwProcessId
- );
- [DllImport("USER32.DLL")]
- public extern static int GetWindowThreadProcessId(
- IntPtr hwnd,
- ref int lpdwProcessId
- );
- //打开进程
- [DllImport("kernel32.dll")]
- public extern static int OpenProcess(
- int dwDesiredAccess,
- int bInheritHandle,
- int dwProcessId
- );
- [DllImport("kernel32.dll")]
- public extern static IntPtr OpenProcess(
- uint dwDesiredAccess,
- int bInheritHandle,
- uint dwProcessId
- );
- //关闭句柄的函数
- [DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
- public static extern int CloseHandle(
- int hObject
- );
- //读内存
- [DllImport("Kernel32.dll ")]
- public static extern Int32 ReadProcessMemory(
- IntPtr hProcess,
- IntPtr lpBaseAddress,
- [In, Out] byte[] buffer,
- int size,
- out IntPtr lpNumberOfBytesWritten
- );
- [DllImport("Kernel32.dll ")]
- public static extern Int32 ReadProcessMemory(
- int hProcess,
- int lpBaseAddress,
- ref int buffer,
- //byte[] buffer,
- int size,
- int lpNumberOfBytesWritten
- );
- [DllImport("Kernel32.dll ")]
- public static extern Int32 ReadProcessMemory(
- int hProcess,
- int lpBaseAddress,
- byte[] buffer,
- int size,
- int lpNumberOfBytesWritten
- );
- //写内存
- [DllImport("kernel32.dll")]
- public static extern Int32 WriteProcessMemory(
- IntPtr hProcess,
- IntPtr lpBaseAddress,
- [In, Out] byte[] buffer,
- int size,
- out IntPtr lpNumberOfBytesWritten
- );
- [DllImport("kernel32.dll")]
- public static extern Int32 WriteProcessMemory(
- int hProcess,
- int lpBaseAddress,
- byte[] buffer,
- int size,
- int lpNumberOfBytesWritten
- );
- //创建线程
- [DllImport("kernel32", EntryPoint = "CreateRemoteThread")]
- public static extern int CreateRemoteThread(
- int hProcess,
- int lpThreadAttributes,
- int dwStackSize,
- int lpStartAddress,
- int lpParameter,
- int dwCreationFlags,
- ref int lpThreadId
- );
- //开辟指定进程的内存空间
- [DllImport("Kernel32.dll")]
- public static extern System.Int32 VirtualAllocEx(
- System.IntPtr hProcess,
- System.Int32 lpAddress,
- System.Int32 dwSize,
- System.Int16 flAllocationType,
- System.Int16 flProtect
- );
- [DllImport("Kernel32.dll")]
- public static extern System.Int32 VirtualAllocEx(
- int hProcess,
- int lpAddress,
- int dwSize,
- int flAllocationType,
- int flProtect
- );
- //释放内存空间
- [DllImport("Kernel32.dll")]
- public static extern System.Int32 VirtualFreeEx(
- int hProcess,
- int lpAddress,
- int dwSize,
- int flAllocationType
- );
- }
- }
【外挂修改器简单介绍】
游戏外挂分为很多种类型,例如本地客户端的内存修改、远程服务器的封包破解。
一般的网游由于服务器的机能限制,并不会将游戏产生的所有数据、计算都提供给服务器来承担。经常的,服务器只会将玩家人物的属性、血量、加点、金钱、装备等重要信息储存在服务器本地,计算结果和数据传递则是通过加密封包来和客户端传输。
因此网游如果封包被破解而被用户发送伪造的封包数据将造成非常严重的后果。
【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手动解除这些保护。