[外挂学习]Jim's游戏外挂学习笔记2——适时编写个读取状态的小程序增加一下士气(原创)
版本:0.13.0402
系统:windows xp
工具:CE5.2+OD1.10+C# 2005
目标:编写获取分析到内存偏移地址的游戏属性的程序
按照学习笔记1中的方法继续查找到了MP,HP上限,HP上限,人物ID,人物姓名等属性,接下来编写一个简单的状态读取程序,语言用C# 2005,程序运行界面如下
项目文件布局如下
以下为各文件简单说明:
1. 内存地址配置文件AddressListConfig.xml,用来存放各级基地址,以及人物各属性的偏移地址
<?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节点
using System;2
using System.Collections.Generic;3
using System.Text;4
using System.Collections;5
using System.Xml;6

7
namespace TLPlayer8
{9
public class AddressListClass : Hashtable10
{11
private string mKey;12

13
public string Key14
{15
get { return mKey; }16
set { mKey = value; }17
}18

19
private int mOffset;20

21
public int Offset22
{23
get { return mOffset; }24
set { mOffset = value; }25
}26

27
private Type mValueType;28

29
public Type ValueType30
{31
get { return mValueType; }32
set { mValueType = value; }33
}34

35
private int mValueLength;36

37
public int ValueLength38
{39
get { return mValueLength; }40
set { mValueLength = value; }41
}42

43
private int mValue;44

45
public int Value46
{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
else85
{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类,内存数据读取用到的类,是核心类,代码如下
using System;2
using System.Collections.Generic;3
using System.Text;4
using System.Diagnostics;5
using System.Runtime.InteropServices;6

7
namespace TLPlayer8
{9
public class MemoryClass10
{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[i] * 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,该类用于存储游戏人物的各属性值
using System;2
using System.Collections.Generic;3
using System.Text;4
using System.ComponentModel;5

6
namespace TLPlayer7
{8
public class PlayerClass9
{10
private MemoryClass memory = null;11
private AddressListClass[] addressLists = null;12
private AddressListClass addressList = null;13

14
//游戏中人物ID15
private string mUserId;16

17
[CategoryAttribute("ID Settings"), DescriptionAttribute("人物ID")]18
public string UserId19
{20
get { return mUserId; }21
set { mUserId = value; }22
}23

24
//游戏中人物姓名25
private string mName;26

27
[CategoryAttribute("ID Settings"), DescriptionAttribute("人物名")]28
public string Name29
{30
get { return mName; }31
set { mName = value; }32
}33

34
//HP35
private int mHp;36

37
[CategoryAttribute("ID Settings"), DescriptionAttribute("生命")]38
public int Hp39
{40
get { return mHp; }41
set { mHp = value; }42
}43

44
//MP45
private int mMp;46

47
[CategoryAttribute("ID Settings"), DescriptionAttribute("内力")]48
public int Mp49
{50
get { return mMp; }51
set { mMp = value; }52
}53

54
//HP上限55
private int mMaxHp;56

57
public int MaxHp58
{59
get { return mMaxHp; }60
set { mMaxHp = value; }61
}62

63
//MP上限64
private int mMaxMp;65

66
public int MaxMp67
{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中调用代码如下
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 TLPlayer10
{11
public partial class Form1 : Form12
{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人物各属性的


浙公网安备 33010602011771号