您的位置:首页 > 其它

使用VC2008制作简单大航海外传内存修改器

2010-07-05 17:33 309 查看
作者
:小草

日期
:2010/7/4

始发于 http://bbs.evewind.net/read.php?tid=2815
程序源代码下载地址: http://code.google.com/p/vcpluslvlup/downloads/list

此教程是本人游戏之作
,如有雷同
,纯属抄袭

此教程宗旨
:动手实践为主
,理论解释为辅
.只看不动手者
,只动手不自学者
,请回家
. 编程乃至学习这件事不适合你
.

简介
:

此教程目的是为了记录我个人的学习成果
,并希望阅读的人掌握文中提到
的知识
.

希望通过这样的方式,教会大家怎么样制作内存修改器,期待有抛砖引
玉的效果.


程分为2
个部分
:

1.
介绍游戏内存修改器主要用到的
WINAPI 函数
.

2. 通过图文一步步介绍如何使用
VC9创建修改器的图形界面
GUI和如何在其中使用第一部分提到的函数
.

使用到的工具:

Visual Studio 2008

作品功能:读取和写入大航海外传女主角的金币数

第一部分
: 介
绍游戏修改器主要用到的
WIN API 函数

游戏修改主要调用的
WIN API 函数有以下几个
:

HANDLE
WINAPI CreateToolhelp32Snapshot(

__in DWORD dwFlags
,

__in DWORD th32ProcessID

);

功能
:CreateToolhelp32Snapshot
()函数用来创建系统快照以得到所有进程的信息
,然后结
合下面的
Thread32First()和
Thread32Next()来枚举进程信息
. 如何使用在这部分的后面会展示
,解释就不必了
.通常越解释越不明白
,直接看实例会更好理解
.下面的函数也一样
.

BOOL
WINAPI Thread32First(

__in HANDLE hSnapshot
,

__in_out
LPTHREADENTRY32 lpte

);

功能
:取得第一个进程信息

BOOL WINAPI Thread32Next(

__in
HANDLE hSnapshot
,

__out LPTHREADENTRY32 lpte

);

功能
:取得下一个进程信息

DWORD
GetWindowThreadProcessId(

HWND hWnd, // handle to window

LPDWORD
lpdwProcessId // address of variable for process identifier

);

功能
:GetWindowThreadProcessId()函数用
来得到我们所要修改的游戏窗口的句柄
(ID).为什么需要得到这个句柄呢
? 答案是下面这个函数需要这个句柄才能让我们的修改器和游戏联系起来
.

HANDLE
OpenProcess(

DWORD dwDesiredAccess, // access flag

BOOL
bInheritHandle, // handle inheritance flag

DWORD dwProcessId //
process identifier

);

功能
:OpenProcess()函数用来打开我们的修改程序和游戏之间的通道
.第一个参数是对我们所
要修改的游戏的内存区域进行修改的权限
,一般当然是越高越好
.

BOOL
WriteProcessMemory(

HANDLE hProcess, // handle of process whose
memory is written to

LPVOID lpBaseAddress, // address to start
writing to

LPVOID lpBuffer, // address of buffer to write data to

DWORD
cbWrite, // number of bytes to write

LPDWORD lpNumberOfBytesWritten
// actual number of bytes written

);

功能
:WriteProcessMemory()函数进行内存的读取工作
.这个函数和下面的写入函数是内存修改器的灵
魂函数
.

BOOL ReadProcessMemory(

HANDLE hProcess, // handle of
process whose memory is read

LPVOID lpBaseAddress, // address to
start reading

LPVOID lpBuffer, // address of buffer to read data to

DWORD
cbRead, // number of bytes to read

LPDWORD lpNumberOfBytesWritten
// actual number of bytes read

);

功能
:ReadProcessMemory()函数进行内存的写入工作
.

好吧
,一般没学过
WINAPI的人看到这段代码就开始晕呼呼了吧
.我在第一次看到的时候也一样
.其实不必惊慌
,初学者只要知道有那么几个东西就够了
.并不需要强行把它们都理解或者
记住
.通过实例的练习
,相信你们会很快熟悉
并开始运用它们
.

下面我们就开始进行动手做练习了
.可能有人会说
”怎么这么快
,你基本什么都没说呢
!”.可是要知道
,就算我说得再多
,还不如亲手动手来实践学习得快
.很多人学习了
n年的
C++和
VC,连一个简单的程序都做不出来
.为什么
?因为只学习理论而不动手实践
.我的宗旨是
:不能真正动手写出实实在在程序的
,不叫学习
,请回家
.因为
,所以
,我们开始动手吧
.

为了更好的理解这些函数的运用和它们之间的联系
,下面的程序使用控制
台为平台
.而不使用会混淆视听的图形界面
(GUI).至于我为什么这么做
,读者们在看了后面的部分会明白的
.

运行
VS2008后
,按照文件
(File)->新建
(New)->项目
(Project)的顺序打开新建项目对话框
. 在项目类型里面选择
VisualC++下的
CLR然后在模板里选择
CLR Console Application.在
名字
(Name)中输入
KoukaigdTrainer.在储存地址
(Location)中输入
D:/Koukaigd
trainer,如果你要存在其他盘如
C盘中就把地址中

D替换成
C.以后类似这样琐碎的事情我就
省略了
.如图
1显示
:



点击
OK后
,进入代码编辑窗口
.

目前我们的控制台程序代码如下
:

//KoukaigdTrainer.cpp
: main project file.

// Author:

小草

// Date:2010/7/4

#include

"stdafx.h"

using

namespace
System;

int

main(array
<System::String
^> ^args)

{

Console::WriteLine(L"Hello World"
);

return
0;

}

如果现在编译并运行这个程序
,一个经典的
Hello
World!程序就出现在我们面前
.以后我们就在上面代码
黄色部分进行操作
.如果想知道每行代码是什么作用请阅读
C++/CLR的相关书籍
.

一般来说
,编写一个程序的步骤分为
:

1. 声明
(declare)成员变量
(variables)和成员函数
(Functions)

2.
编写成员函数的代码使其达成我们的目的


么我们现在就来声明我们上面我们提到的函数以及我们将要用到的变量
.

//KoukaigdTrainer.cpp
: main project file.

// Author:

小草

// Date:2010/7/4

#include

"stdafx.h"

#include

<windows.h>

using

namespace
System;

int

main(array
<System::String
^> ^args)

{

//Console::WriteLine(L"HelloWorld");

/////////////////////////////////////

//

声明变量以及函数

//

声明int变量以储存我们将要修改
的数值:金钱

unsigned
int
mainCharMoney = 0;

//

声明bool变量以储存玩家是否想锁定金钱数

bool

doFreezeMoney = 0;

//

定义金钱在内存中的地址

LPVOID money_addr = (void
*) 0x004c023a;

//

定义游戏在内存中的名字

const
wchar_t
* p_GameTitle = L"koukaigd.exe"
;

/////////////////////////////////////

return
0;

}

好了
,基本的变量声明好以后
,我们开始添加程序的主体
.

//KoukaigdTrainer.cpp
: main project file.

// Author:

小草

// Date:2010/7/4

#include

"stdafx.h"

#include

<windows.h>

#include

<tlhelp32.h>

using

namespace
System;

int

main(array
<System::String
^> ^args)

{

//Console::WriteLine(L"HelloWorld");

/////////////////////////////////////

//

声明变量以及函数

//

声明int变量以储存我们将要修改
的数值:金钱

unsigned
int
mainCharMoney = 0;

//

声明bool变量以储存玩家是否想锁定金钱数

bool
doFreezeMoney
= 0;

//

定义金钱在内存中的地址

LPVOID money_addr = (void
*) 0x004c023a;

//

定义游戏在内存中的名字

const
wchar_t
* p_GameTitle = L"koukaigd.exe"
;

/////////////////////////////////////

//

读取进程信息区

//

定义上面提到的个取得进程在内存里信息的变量

HANDLE hProcessSnap; //

这个变量将储存所有进程的信息

HANDLE hProcess = NULL; //

这个变量将储存大航海外传的进程信息,今后对于内存的读写都是通过它来进行操作的

PROCESSENTRY32 pe32; //

储存某一个进程的基础信息,也就是储存大航海外传的进程的基础信息,将来用于得到进程的句柄(ID)

hProcessSnap = CreateToolhelp32Snapshot(
TH32CS_SNAPPROCESS, 0 ); //

创建系统进程快照

pe32.dwSize = sizeof
( PROCESSENTRY32 ); //

定义进程信息结构

Process32First(hProcessSnap, &pe32); //

读取第一个进程的信息然后储存到
pe32这个变量中

//

/////////////////////////////////////

/////////////////////////////////////

//

通过一个循环找到大航海外传的进程
并打开修改器和游戏间的通道

do

{ //pe32.szExeFile

的操作结果就是取得当前映像名称

if
(_wcsicmp(pe32.szExeFile,p_GameTitle)
== 0) //

通过对比获得的映像名称和我们已知道
的游戏名称(koukaigd.exe),确定找到正确的进程

{

//

第一个参数的意思是取得读写的权限,第二个参数不必深究,有兴趣自己
查询winapi,第三个参数是当前进程的句柄

hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE,
pe32.th32ProcessID); //

打开进程,并把进程储存起来,使得
修改器可以对进程内部进行操作

Console::WriteLine(L"

检测到大航
海外传正在运行.../n"

);

break
; //

跳出循环

}

}

while
(Process32Next(hProcessSnap,&pe32));
//

循环直到找到大航海外传进程为止

CloseHandle(
hProcessSnap ); //

关闭进程快照

if
(hProcess== NULL) //

如果进程没找到时的操作,比如你在运行游戏前运行了修改器

{

printf("

大航海外传
没有运行... /n"

);

Console::ReadLine(); //

停顿程序,让使用者知道发生了什么事

}

else

{

//

第一个参数为当前游戏大航海外传的进
程信息,第二个参数是女主角的金币的内存位置,第三个参数是变量的值,第四个参数是读取的byte,第五个不必深究,有兴趣自己查winapi

//ReadProcessMemory()

就是把在money_addr内存
地址里的值存放到mainCharMoney这个变量中然后在下面的Console::WriteLine()中显示给修改器用户看

ReadProcessMemory(hProcess,
money_addr,&mainCharMoney, 4, NULL);

Console::WriteLine("

主角现有金币: "

+ mainCharMoney + "/n"
);

//

提示用户想把多少的数值写入到内存中


Console::WriteLine("

修改主角金
币数额为(0-4000000000): "

);

mainCharMoney =
Convert::ToInt32(Console::ReadLine()); //

变量类型转换,自己查资料吧

//

第一个参数为当前游戏大航海外传的进
程信息,第二个参数是女主角的金币的内存位置,第三个参数是变量的值,第四个参数是写入的byte,第五个不必深究,有兴趣自己查winapi

//WriteProcessMemory()

所做的就是把
mainCharMoney这个变量中的值写入到money_addr这个内存地址里面去

WriteProcessMemory(hProcess,
money_addr,&mainCharMoney, 4, NULL);

Console::WriteLine("

主角现有金
币: "

+
mainCharMoney + "/n"
);

Console::ReadLine();

}

CloseHandle(hProcess); //

关闭进程

return
0;

}

如果因为排版问题觉得比较混乱的话
,我在最后会附上源码的压缩文件
,用
VS2008打开后的内建编辑器看会好很多
.

每条语句我都写了解释
,自己慢慢琢磨吧
.现在通过编译这个程序
.运行大航海外传后开始女主角的旅程
.然后运行这个程序
.按照提示输入数字然后回车后
.程序结束
,返回游戏进出一个设施使得游戏数值刷新
.你就会发现女主角的金币数量已经被修改了
.

至此
,游戏内存修改的核心已经完成
,下一步就是为我们的修改器做一个图形界

(GUI)了
.

第二部分
:创建
GUI型游戏修改器

在上一部分的写作时
,由于是自学
,走了不少弯路
. 有另外一个更简便的方法找到进程的句柄
(ID),然后进而打开进程的
.下面就采用这个方法
.

用到的方法
:

public:

static
array<Process^>^GetProcessesByName(

String^processName,

String^machineName

)

然后调用这个方法必须要写在预处理那里的代码
:

#using <System.dll>

using namespace System;

using
namespace System::Diagnostics;

using namespace
System::ComponentModel;

使用例子
:

array<Process^>^
currentProcess =Process::GetProcessesByName( "koukaigd" );

HANDLE
hProcess =OpenProcess(PROCESS_ALL_ACCESS, FALSE,
currentProcess[0]->Id);

解释
:第一行创建一个
Process类的数组
,然后在其中写入进程名字和
GetProcessesByName()参
数中的字符串相同的进程
.第二行通过取得的第一个进程的
ID属性取得进程句柄
(ID).进而打开游戏进程
. 此后操作与前一个控制台程序一样
.




图所示
,创建一个
CLR的

Windows Forms Application

项目创建好后
,我们通过添加
2个按钮
,1个文本框和
1个标签共
4个控件
,来完成我们在第一部分的功能
:

1. 读取并显示数据

2. 写入数据




2个按钮控件的
Name属性分别改为
btnRead和
btnWrite相应的把它们的
Text属性改为读取和写入
.标签的
Name属性改为
lbMoney还有
Text属性改为金币
.文本框的
Name属性改为
txtMoney其
Text属性留空
. 最终结果如上图所示
.

添加控件非常简单
,只需要把相应的控件从控件菜单中用鼠标拖拽到窗体中即可
,具体步骤我
就不详细说了
,不懂的话
,可以自己在网络上
搜索相应的教程
.我们主要把精力集中在如何实现功能上
.

现在我们其实有一个问题就是什么时候读取数据
.我们有
2个选择
,一个是在修改器运行后马上进行读取
,一个是当我们想要修改器读取的时候才读取
.

不过我
们既然有
2个按钮分别为读取和写入
,那么我
们就选择用后者来完成我们的程序
.

我们先来实现读取的功能
.就像第一部分一样
,我们需要先定义一些变量以便我们使用
.

#pragma

once

#using

"System.dll"

#include

<windows.h>

namespace

KoukaigdTrainerGUI {

using
namespace
System;

using
namespace
System::ComponentModel;

using
namespace
System::Collections;

using
namespace
System::Windows::Forms;

using
namespace
System::Data;

using
namespace
System::Drawing;

using
namespace
System::Diagnostics;

using
namespace
System::ComponentModel;

/////////////////////////////////////

//

声明变量以及函数

//

声明int变量以储存我们将要修改
的数值:金钱

unsigned
int
mainCharMoney
= 0;

//

定义金钱在内存中的地址

LPVOID money_addr = (void
*)0x004c023a;

///
<summary>

/// Summary forForm1

///

///

省略下面代码

把第一部分的声明按照上面的顺序添加到相应的位置
,我
们就可以调用
OpenProcess等函数了
.


Form1视图中双击读取按钮
,进入这个按钮的鼠标点击事件
,添加代码如下
:

private

: System::VoidbtnRead_Click(System::Object^
sender,System::EventArgs^ e)

{

array
<Process^>^ currentProcess =
Process::GetProcessesByName( "koukaigd"
);

HANDLE hProcess
=OpenProcess(PROCESS_ALL_ACCESS, FALSE, currentProcess[0]->Id);

ReadProcessMemory(hProcess,money_addr,
&mainCharMoney, 4, NULL);

txtMoney->Text=mainCharMoney.ToString();

CloseHandle(hProcess);

}

添加的代码和第一部分没有区别
,只是顺序变了一下
.又因为这个按钮只是负责读取数据
,所以只调用了
ReadProcessMemory()函数
.

按照上面的步骤添加写入按钮的事件
,添加代码如下
:

private

: System::VoidbtnWrite_Click(System::Object^
sender,System::EventArgs^ e)

{

array
<Process^>^currentProcess =
Process::GetProcessesByName( "koukaigd"
);

HANDLE hProcess
=OpenProcess(PROCESS_ALL_ACCESS, FALSE, currentProcess[0]->Id);

mainCharMoney
=Convert::ToUInt32(txtMoney->Text);

WriteProcessMemory(hProcess,
money_addr,&mainCharMoney, 4, NULL);

CloseHandle(hProcess);

}

至此
,整个
GUI修改器就做好了
.编译运行以后
,运行大航海外传选女主角
,可以直接改金币数
.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: