作者:Jim's
支持原创,经典收藏!
这贴之前有朋友发过,东西很全面,所以整理了一下发出来,应该对新来的朋友有帮助!
[外挂学习]Jim's游戏外挂学习笔记2——适时编写个读取状态的小程序增加一下士气
游戏:天龙八部
版本:0.13.0402
系统:windows xp
工具:CE5.2+OD1.10+C# 2005
目标:编写获取分析到内存偏移地址的游戏属性的程序
按照学习笔记1中的方法继续查找到了MP,HP上限,HP上限,人物ID,人物姓名等属性,接下来编写一个简单的状态读取程序,语言用C# 2005,程序运行界面如下
项目文件布局如下
以下为各文件简单说明:
1. 内存地址配置文件AddressListConfig.xml,用来存放各级基地址,以及人物各属性的偏移地址
1<?xml version="1.0" encoding="utf-8" ?>
2<Configs>
3 <AddressList Key="Base1" Offset="0x013D2BD8" ValueType="System.Int32" ValueLength="4">
4 <AddressList Key="Base1.Base2" Offset="0x12C" ValueType="System.Int32" ValueLength="4">
5 <AddressList Key="Base1.Base2.MyPlayer" Offset="0x8" ValueType="System.Int32" ValueLength="4">
6 <Address Key="Base1.Base2.MyPlayer.UserId" Offset="0" ValueType="System.String" ValueLength="4" />
7 <Address Key="Base1.Base2.MyPlayer.Name" Offset="0x10" ValueType="System.String" ValueLength="12" />
8 <Address Key="Base1.Base2.MyPlayer.Hp" Offset="0x6B8" ValueType="System.Int32" ValueLength="4" />
9 <Address Key="Base1.Base2.MyPlayer.Mp" Offset="0x6BC" ValueType="System.Int32" ValueLength="4" />
10 <Address Key="Base1.Base2.MyPlayer.MaxHp" Offset="0x81C" ValueType="System.Int32" ValueLength="4" />
11 <Address Key="Base1.Base2.MyPlayer.MaxMp" Offset="0x820" ValueType="System.Int32" ValueLength="4" />
12 </AddressList>
13 </AddressList>
14 </AddressList>
15</Configs>
1) AddressList为嵌套的基地址
Key为全局的读取键名,Offset为学习笔记1中查到的偏移量,ValueType属性为此地址存放的值对应.net中的数据类型,ValueLength为读取内存长度值
2) Address节点中存放的是各游戏属性对应的地址
各属性与AddressList类似
2. 基地址类AddressListClass,对应XML中的AddressListClass节点
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Collections;
5using System.Xml;
6
7namespace TLPlayer
8{
9 public class AddressListClass : Hashtable
10 {
11 private string mKey;
12
13 public string Key
14 {
15 get { return mKey; }
16 set { mKey = value; }
17 }
18
19 private int mOffset;
20
21 public int Offset
22 {
23 get { return mOffset; }
24 set { mOffset = value; }
25 }
26
27 private Type mValueType;
28
29 public Type ValueType
30 {
31 get { return mValueType; }
32 set { mValueType = value; }
33 }
34
35 private int mValueLength;
36
37 public int ValueLength
38 {
39 get { return mValueLength; }
40 set { mValueLength = value; }
41 }
42
43 private int mValue;
44
45 public int Value
46 {
47 get { return mValue; }
48 set { mValue = value; }
49 }
50
51
52 private void AddChild(AddressListClass childAddressList)
53 {
54 this.Add(childAddressList.Key, childAddressList);
55 }
56
57 private void AddChild(AddressClass childAddress)
58 {
59 this.Add(childAddress.Key, childAddress);
60 }
61
62 //从配置文件里获取配置
63 public void LoadConfig(string fileName)
64 {
65 XmlDocument xmlDoc = new XmlDocument();
66 xmlDoc.Load(fileName);
67 XmlNode currentNode = xmlDoc.DocumentElement.SelectSingleNode(string.Format("AddressList[@Key='{0}']", this.Key));
68 this.Key = currentNode.Attributes["Key"].Value;
69 this.Offset = Convert.ToInt32(currentNode.Attributes["Offset"].Value, 16);
70 this.ValueType = Type.GetType(currentNode.Attributes["ValueType"].Value);
71 this.ValueLength = Convert.ToInt32(currentNode.Attributes["ValueLength"].Value);
72 LoadConfigFromNode(this, currentNode);
73 }
74
75 //获取某节点
76 public AddressListClass GetAddressList(string key)
77 {
78 foreach (string s in this.Keys)
79 {
80 if (s == key)
81 {
82 return (AddressListClass)this[s];
83 }
84 else
85 {
86 return ((AddressListClass)this[s]).GetAddressList(key);
87 }
88 }
89 return null;
90 }
91
92 //获取某叶子
93 public AddressClass GetChildAddress(string key)
94 {
95 foreach (string s in this.Keys)
96 {
97 if (s == key)
98 {
99 return (AddressClass)this[s];
100 }
101 }
102 return null;
103 }
104
105 //递归加载所有节点
106 private void LoadConfigFromNode(AddressListClass addressList, XmlNode node)
107 {
108 foreach (XmlNode childNode in node.ChildNodes)
109 {
110 if (childNode.Name == "AddressList")
111 {
112 AddressListClass childAddressList = new AddressListClass();
113 childAddressList.Key = childNode.Attributes["Key"].Value;
114 childAddressList.Offset = Convert.ToInt32(childNode.Attributes["Offset"].Value, 16);
115 childAddressList.ValueType = Type.GetType(childNode.Attributes["ValueType"].Value);
116 childAddressList.ValueLength = Convert.ToInt32(childNode.Attributes["ValueLength"].Value);
117 addressList.AddChild(childAddressList);
118 LoadConfigFromNode(childAddressList, childNode);
119 }
120 else if (childNode.Name == "Address")
121 {
122 AddressClass childAddress = new AddressClass();
123 childAddress.Key = childNode.Attributes["Key"].Value;
124 childAddress.Offset = Convert.ToInt32(childNode.Attributes["Offset"].Value, 16);
125 childAddress.ValueType = Type.GetType(childNode.Attributes["ValueType"].Value);
126 childAddress.ValueLength = Convert.ToInt32(childNode.Attributes["ValueLength"].Value);
127 addressList.AddChild(childAddress);
128 }
129 }
130 }
131 }
132}
133
该类的属性为XML文件中对应的节点属性,多出来的Value属性是用来临时存放该地址对应的内存值的,主要方法是LoadConfig方法,从XML中读取各属性和关系
3. AddressClass类,类似AddressListClass类,作用是存放AddressClass节点的配置,即各游戏属性所在地址的配置,该类只有属性没有方法
4. MemoryClass类,内存数据读取用到的类,是核心类,代码如下
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Diagnostics;
5using System.Runtime.InteropServices;
6
7namespace TLPlayer
8{
9 public class MemoryClass
10 {
11 [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
12 public static extern int OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
13
14 [DllImport("kernel32.dll", SetLastError = true)]
15 static extern int CloseHandle(int hProcess);
16
17 [DllImport("kernel32.dll", SetLastError = true)]
18 static extern int ReadProcessMemory(int hProcess, IntPtr lpBaseAddress, [In, Out] byte[] lpBuffer, int nSize, ref int lpNumberOfBytesWritten);
19
20 private int hProcess;
21
22 private byte[] buffer;
23
24 private int lpNumberOfBytesWritten = 0;
25
26 public void Init()
27 {
28 Process[] ps = Process.GetProcessesByName("game");
29 if (ps.Length == 0)
30 {
31 throw new Exception("游戏未打开!");
32 }
33 Process p = ps[0];
34
35 hProcess = OpenProcess(0x0010, true, p.Id);
36 if (hProcess <= 0)
37 {
38 throw new Exception("进程打开失败!");
39 }
40 }
41
42 public void Dispose()
43 {
44 if(hProcess> 0)
45 CloseHandle(hProcess);
46 }
47
48 public int ReadInt(int address,int length)
49 {
50 if (length > 4)
51 length = 4;
52 if(length < 1)
53 length = 4;
54 buffer = new byte[length];
55 int r = ReadProcessMemory(hProcess, (IntPtr)address, buffer, length, ref lpNumberOfBytesWritten);
56 if (r == 0)
57 {
58 throw new Exception("读取内存错误!");
59 }
60 r = 0;
61 for (int i = 0; i < length; i++)
62 {
63 r += buffer * ComputeExp(256, i);
64 }
65 return r;
66 }
67
68 public string ReadString(int address, int length)
69 {
70 buffer = new byte[length];
71 int r = ReadProcessMemory(hProcess, (IntPtr)address, buffer, length, ref lpNumberOfBytesWritten);
72 if (r == 0)
73 {
74 throw new Exception("读取内存错误!");
75 }
76 return Encoding.GetEncoding("gb2312").GetString(buffer);
77 }
78
79 private int ComputeExp(int i, int j)
80 {
81 int r = 1;
82 for (int o = 0; o < j; o++)
83 {
84 r *= i;
85 }
86 return r;
87 }
88 }
89}
90
该类中读取内存数据的原理是先用OpenProcess打开游戏进程,再用ReadProcessMemory方法去读,最后CloseHandle方法释放资源,进程操作使用.net framework的Process类
5. 游戏人物类PlayerClass,该类用于存储游戏人物的各属性值
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.ComponentModel;
5
6namespace TLPlayer
7{
8 public class PlayerClass
9 {
10 private MemoryClass memory = null;
11 private AddressListClass[] addressLists = null;
12 private AddressListClass addressList = null;
13
14 //游戏中人物ID
15 private string mUserId;
16
17 [CategoryAttribute("ID Settings"), DescriptionAttribute("人物ID")]
18 public string UserId
19 {
20 get { return mUserId; }
21 set { mUserId = value; }
22 }
23
24 //游戏中人物姓名
25 private string mName;
26
27 [CategoryAttribute("ID Settings"), DescriptionAttribute("人物名")]
28 public string Name
29 {
30 get { return mName; }
31 set { mName = value; }
32 }
33
34 //HP
35 private int mHp;
36
37 [CategoryAttribute("ID Settings"), DescriptionAttribute("生命")]
38 public int Hp
39 {
40 get { return mHp; }
41 set { mHp = value; }
42 }
43
44 //MP
45 private int mMp;
46
47 [CategoryAttribute("ID Settings"), DescriptionAttribute("内力")]
48 public int Mp
49 {
50 get { return mMp; }
51 set { mMp = value; }
52 }
53
54 //HP上限
55 private int mMaxHp;
56
57 public int MaxHp
58 {
59 get { return mMaxHp; }
60 set { mMaxHp = value; }
61 }
62
63 //MP上限
64 private int mMaxMp;
65
66 public int MaxMp
67 {
68 get { return mMaxMp; }
69 set { mMaxMp = value; }
70 }
71
72 public PlayerClass(AddressListClass rootAddressList, MemoryClass memory)
73 {
74 this.memory = memory;
75 addressLists = new AddressListClass[3];
76 addressLists[0] = rootAddressList;
77 addressLists[1] = rootAddressList.GetAddressList("Base1.Base2");
78 addressLists[2] = addressLists[1].GetAddressList("Base1.Base2.MyPlayer");
79 addressList = addressLists[2];
80 if (addressList == null)
81 {
82 throw new Exception("没有Player的地址配置!");
83 }
84 }
85
86 public void LoadFromMemory()
87 {
88 if (memory == null)
89 return;
90
91 addressLists[0].Value = memory.ReadInt(addressLists[0].Offset, 4);
92 addressLists[1].Value = memory.ReadInt(addressLists[0].Value + addressLists[1].Offset, 4);
93 addressLists[2].Value = memory.ReadInt(addressLists[1].Value + addressLists[2].Offset, 4);
94
95 foreach (object o in addressList.Values)
96 {
97 if (o is AddressClass)
98 {
99 AddressClass a = (AddressClass)o;
100 switch (a.Key)
101 {
102 case "Base1.Base2.MyPlayer.UserId":
103 this.UserId = memory.ReadInt(addressList.Value + a.Offset, a.ValueLength).ToString("X");
104 break;
105 case "Base1.Base2.MyPlayer.Name":
106 this.Name = memory.ReadString(addressList.Value + a.Offset, a.ValueLength);
107 break;
108 case "Base1.Base2.MyPlayer.Hp":
109 this.Hp = memory.ReadInt(addressList.Value + a.Offset, a.ValueLength);
110 break;
111 case "Base1.Base2.MyPlayer.Mp":
112 this.Mp = memory.ReadInt(addressList.Value + a.Offset, a.ValueLength);
113 break;
114 case "Base1.Base2.MyPlayer.MaxHp":
115 this.MaxHp = memory.ReadInt(addressList.Value + a.Offset, a.ValueLength);
116 break;
117 case "Base1.Base2.MyPlayer.MaxMp":
118 this.MaxMp = memory.ReadInt(addressList.Value + a.Offset, a.ValueLength);
119 break;
120 }
121 }
122 }
123 }
124 }
125}
126
该类各属性对应游戏里人物的属性,演示程序只设置几个已找到内存偏移地址的属性
实例化时关联上相关的AddressListClass类以便后面获取各属性当前地址,进而获取各地址对应的值
主要方法只有一个是LoadFromMemory,从当前内存中加载该类的各属性,原理是用各属性对应的Key值去配置里搜索到偏移地址,然后通过3级偏移地址得到各属性的值
6. 主程序Form1中调用代码如下
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Data;
5using System.Drawing;
6using System.Text;
7using System.Windows.Forms;
8
9namespace TLPlayer
10{
11 public partial class Form1 : Form
12 {
13 private MemoryClass memory;
14 private AddressListClass addressList;
15 private PlayerClass player;
16
17 public Form1()
18 {
19 InitializeComponent();
20 }
21
22 private void Form1_Load(object sender, EventArgs e)
23 {
24 addressList = new AddressListClass();
25 addressList.Key = "Base1";
26 addressList.LoadConfig(Application.StartupPath + "//AddressListConfig.xml");
27
28 memory = new MemoryClass();
29 memory.Init();
30
31 player = new PlayerClass(addressList, memory);
32
33 pg.SelectedObject = player;
34 }
35
36 private void button1_Click(object sender, EventArgs e)
37 {
38 player.LoadFromMemory();
39 pg.Refresh();
40 }
41
42 private void Form1_FormClosing(object sender, FormClosingEventArgs e)
43 {
44 memory.Dispose();
45 }
46 }
47}
没什么好说的,依次调用各类的相关方法就好
其中pg是个PropertyGrid对象,button1是用来手动reload人物各属性的