游戏:天龙八部 版本:0.13.0402 系统:windows xp 工具:CE5.2+OD1

游戏:天龙八部
版本: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" ValueLe
ngth="4">
4
<AddressList Key="Base1.Base2" Offset="0x12C" ValueType="System.Int32" Value
Length="4">
5
<AddressList Key="Base1.Base2.MyPlayer" Offset="0x8" ValueType="System.Int
32" 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="Syst
em.String" ValueLength="12" />
8
<Address Key="Base1.Base2.MyPlayer.Hp" Offset="0x6B8" ValueType="Syste
m.Int32" ValueLength="4" />
9
<Address Key="Base1.Base2.MyPlayer.Mp" Offset="0x6BC" ValueType="Syste
m.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="S
ystem.Int32" ValueLength="4" />
12
</AddressList>
13
14
</AddressList>
</AddressList>
15 </Configs>
1) AddressList 为嵌套的基地址
Key 为全局的读取键名,Offset 为学习笔记 1 中查到的偏移量,ValueType 属性为此地
址存放的值对应.net 中的数据类型,ValueLength 为读取内存长度值
2) Address 节点中存放的是各游戏属性对应的地址
各属性与 AddressList 类似
2. 基地址类 AddressListClass,对应 XML 中的 AddressListClass 节点
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Collections;
5 using System.Xml;
6
7 namespace 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.For
mat("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"].Valu
e);
72
73
LoadConfigFromNode(this, currentNode);
}
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"].Val
ue, 16);
115
childAddressList.ValueType = Type.GetType(childNode.Attributes["ValueTy
pe"].Value);
116
childAddressList.ValueLength = Convert.ToInt32(childNode.Attributes["Valu
eLength"].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"].Valu
e, 16);
125
"].Value);
childAddress.ValueType = Type.GetType(childNode.Attributes["ValueType
126
childAddress.ValueLength = Convert.ToInt32(childNode.Attributes["ValueL
ength"].Value);
127
addressList.AddChild(childAddress);
128
}
129
}
130
}
131
}
132 }
133
该类的属性为 XML 文件中对应的节点属性,多出来的 Value 属性是用来临时存放该地址对
应的内存值的,主要方法是 LoadConfig 方法,从 XML 中读取各属性和关系
3. AddressClass 类,类似 AddressListClass 类,作用是存放 AddressClass 节点的配置,
即各游戏属性所在地址的配置,该类只有属性没有方法
4. MemoryClass 类,内存数据读取用到的类,是核心类,代码如下
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Diagnostics;
5 using System.Runtime.InteropServices;
6
7 namespace 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 d
wProcessId);
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] b
yte[] 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
46
CloseHandle(hProcess);
}
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 lpNumb
erOfBytesWritten);
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[i] * ComputeExp(256, i);
64
}
65
return r;
66
}
67
68
69
public string ReadString(int address, int length)
{
70
buffer = new byte[length];
71
int r = ReadProcessMemory(hProcess, (IntPtr)address, buffer, length, ref lpNumb
erOfBytesWritten);
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,该类用于存储游戏人物的各属性值
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.ComponentModel;
5
6 namespace 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
87
88
89
public void LoadFromMemory()
{
if (memory == null)
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
96
97
98
foreach (object o in addressList.Values)
{
if (o is AddressClass)
{
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.ValueL
ength).ToString("X");
104
105
106
break;
case "Base1.Base2.MyPlayer.Name":
this.Name = memory.ReadString(addressList.Value + a.Offset, a.Valu
eLength);
107
108
109
break;
case "Base1.Base2.MyPlayer.Hp":
this.Hp = memory.ReadInt(addressList.Value + a.Offset, a.ValueLengt
h);
110
111
112
break;
case "Base1.Base2.MyPlayer.Mp":
this.Mp = memory.ReadInt(addressList.Value + a.Offset, a.ValueLengt
h);
113
114
115
break;
case "Base1.Base2.MyPlayer.MaxHp":
this.MaxHp = memory.ReadInt(addressList.Value + a.Offset, a.ValueL
ength);
116
break;
117
case "Base1.Base2.MyPlayer.MaxMp":
118
this.MaxMp = memory.ReadInt(addressList.Value + a.Offset, a.ValueL
ength);
119
break;
120
}
121
}
122
}
123
}
124
}
125 }
126
该类各属性对应游戏里人物的属性,演示程序只设置几个已找到内存偏移地址的属性
实例化时关联上相关的 AddressListClass 类以便后面获取各属性当前地址,进而获取各地
址对应的值
主要方法只有一个是 LoadFromMemory,从当前内存中加载该类的各属性,原理是用各属
性对应的 Key 值去配置里搜索到偏移地址,然后通过 3 级偏移地址得到各属性的值
6. 主程序 Form1 中调用代码如下
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Text;
7 using System.Windows.Forms;
8
9 namespace 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
20
InitializeComponent();
}
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
34
pg.SelectedObject = player;
}
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 人物各属性的