2007-06-21 15:37
> >
更多精彩技术文章>
游戏:天龙八部 版本: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';}" οnclick="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" οnlοad="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';}" οnclick="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" οnlοad="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';}" οnclick="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" οnlοad="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';}" οnclick="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" οnlοad="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人物各属性的 > > 更多精彩技术文章 |