游戏:天龙八部
版本:0.13.0402
系统:windows xp
工具:CE5.2+OD1.10+C# 2005
目标:编写获取分析到内存偏移地址的游戏属性的程序
按照学习笔记1中的方法继续查找到了MP,HP上限,HP上限,人物ID,人物姓名等属性,接下来编写一个简单的状态读取程序,语言用C# 2005,程序运行界面如下
项目文件布局如下
以下为各文件简单说明:
1. 内存地址配置文件AddressListConfig.xml,用来存放各级基地址,以及人物各属性的偏移地址
1[img][/img]<?xml version="1.0" encoding="UTF-8" ?> 2[img][/img]<Configs> 3[img][/img] <AddressList Key="Base1" Offset="0x013D2BD8" ValueType="System.Int32" ValueLength="4"> 4[img][/img] <AddressList Key="Base1.Base2" Offset="0x12C" ValueType="System.Int32" ValueLength="4"> 5[img][/img] <AddressList Key="Base1.Base2.MyPlayer" Offset="0x8" ValueType="System.Int32" ValueLength="4"> 6[img][/img] <Address Key="Base1.Base2.MyPlayer.UserId" Offset="0" ValueType="System.String" ValueLength="4" /> 7[img][/img] <Address Key="Base1.Base2.MyPlayer.Name" Offset="0x10" ValueType="System.String" ValueLength="12" /> 8[img][/img] <Address Key="Base1.Base2.MyPlayer.Hp" Offset="0x6B8" ValueType="System.Int32" ValueLength="4" /> 9[img][/img] <Address Key="Base1.Base2.MyPlayer.Mp" Offset="0x6BC" ValueType="System.Int32" ValueLength="4" />10[img][/img] <Address Key="Base1.Base2.MyPlayer.MaxHp" Offset="0x81C" ValueType="System.Int32" ValueLength="4" />11[img][/img] <Address Key="Base1.Base2.MyPlayer.MaxMp" Offset="0x820" ValueType="System.Int32" ValueLength="4" />12[img][/img] </AddressList>13[img][/img] </AddressList>14[img][/img] </AddressList>15[img][/img]</Configs>
1) AddressList为嵌套的基地址
Key为全局的读取键名,Offset为学习笔记1中查到的偏移量,ValueType属性为此地址存放的值对应.net中的数据类型,ValueLength为读取内存长度值
2) Address节点中存放的是各游戏属性对应的地址
各属性与AddressList类似
2. 基地址类AddressListClass,对应XML中的AddressListClass节点
1[img][/img]using System;
2[img][/img]using System.Collections.Generic;
3[img][/img]using System.Text;
4[img][/img]using System.Collections;
5[img][/img]using System.Xml;
6[img][/img]
7[img][/img]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.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
screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif');}" alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0>}133[img][/img]
该类的属性为XML文件中对应的节点属性,多出来的Value属性是用来临时存放该地址对应的内存值的,主要方法是LoadConfig方法,从XML中读取各属性和关系
3. AddressClass类,类似AddressListClass类,作用是存放AddressClass节点的配置,即各游戏属性所在地址的配置,该类只有属性没有方法
4. MemoryClass类,内存数据读取用到的类,是核心类,代码如下
1[img][/img]using System;
2[img][/img]using System.Collections.Generic;
3[img][/img]using System.Text;
4[img][/img]using System.Diagnostics;
5[img][/img]using System.Runtime.InteropServices;
6[img][/img]
7[img][/img]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 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
screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif');}" alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0>}90[img][/img]
该类中读取内存数据的原理是先用OpenProcess打开游戏进程,再用ReadProcessMemory方法去读,最后CloseHandle方法释放资源,进程操作使用.net framework的Process类
5. 游戏人物类PlayerClass,该类用于存储游戏人物的各属性值
1[img][/img]using System;
2[img][/img]using System.Collections.Generic;
3[img][/img]using System.Text;
4[img][/img]using System.ComponentModel;
5[img][/img]
6[img][/img]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 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
screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif');}" alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0>}126[img][/img]
该类各属性对应游戏里人物的属性,演示程序只设置几个已找到内存偏移地址的属性
实例化时关联上相关的AddressListClass类以便后面获取各属性当前地址,进而获取各地址对应的值
主要方法只有一个是LoadFromMemory,从当前内存中加载该类的各属性,原理是用各属性对应的Key值去配置里搜索到偏移地址,然后通过3级偏移地址得到各属性的值
6. 主程序Form1中调用代码如下
1[img][/img]using System;
2[img][/img]using System.Collections.Generic;
3[img][/img]using System.ComponentModel;
4[img][/img]using System.Data;
5[img][/img]using System.Drawing;
6[img][/img]using System.Text;
7[img][/img]using System.Windows.Forms;
8[img][/img]
9[img][/img]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 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
screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" onclick="if(!this.resized) {return true;} else {window.open('http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif');}" alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}" border=0>}
没什么好说的,依次调用各类的相关方法就好
其中pg是个PropertyGrid对象,button1是用来手动reload人物各属性的
> >