//用宏主要是为了实现看到名字知道其意义,且修改时可以很方便
//定义人物各个属性地址宏
#define JINQIANADDRESS 0x0072D1E8
//王小虎属性地址
#define WANGJINGYANADDRESS 0x0072C2DC
#define WANGTILIADDRESS 0x0072C2E4
#define WANGZHENQIADDRESS 0x0072C2F0
#define WANGWUSHUADDRESS 0x0072C300
#define WANGLINGLIADDRESS 0x0072C308
#define WANGFANGYUADDRESS 0x0072C310
#define WANGSHENFAADDRESS 0x0072C318
#define WANGJIYUNADDRESS 0x0072C320
#define WANGJINQIANADDRESS JINQIANADDRESS
//李忆如属性地址
#define LIJINGYANADDRESS 0x0072CE94
#define LITILIADDRESS 0x0072CE9C
#define LIZHENQIADDRESS 0x0072CEA8
#define LIWUSHUADDRESS 0x0072CEB8
#define LILINGLIADDRESS 0x0072CEC0
#define LIFANGYUADDRESS 0x0072CEC8
#define LISHENFAADDRESS 0x0072CED0
#define LIJIYUNADDRESS 0x0072CED8
#define LIJINQIANADDRESS JINQIANADDRESS
//沈欺霜属性地址
#define SHENJINGYANADDRESS 0x0072C6C4
#define SHENTILIADDRESS 0x0072C6CC
#define SHENZHENQIADDRESS 0x0072C6D8
#define SHENWUSHUADDRESS 0x0072C6E8
#define SHENLINGLIADDRESS 0x0072C6F0
#define SHENFANGYUADDRESS 0x0072C6F8
#define SHENSHENFAADDRESS 0x0072C700
#define SHENJIYUNADDRESS 0x0072C708
#define SHENJINQIANADDRESS JINQIANADDRESS
//苏媚属性地址
#define SUJINGYANADDRESS 0x0072CAAC
#define SUTILIADDRESS 0x0072CAB4
#define SUZHENQIADDRESS 0x0072CAC0
#define SUWUSHUADDRESS 0x0072CAD0
#define SULINGLIADDRESS 0x0072CAD8
#define SUFANGYUADDRESS 0x0072CAE0
#define SUSHENFAADDRESS 0x0072CAE8
#define SUJIYUNNADDRESS 0x0072CAF0
#define SUJINQIANADDRESS JINQIANADDRESS
//定义人物数组结构体类型
struct _Person
{
DWORD dwShuXing[9];
};
//定义并初始化人物数组
//用数组存储4个主角的9个属性,可以使用循环来操作它们的值,大大的减少了代码冗余
const _Person stPerson[4] = {
{WANGJINGYANADDRESS, WANGTILIADDRESS, WANGZHENQIADDRESS,
WANGWUSHUADDRESS, WANGLINGLIADDRESS, WANGFANGYUADDRESS,
WANGSHENFAADDRESS, WANGJIYUNADDRESS, WANGJINQIANADDRESS}, //王小虎
{LIJINGYANADDRESS, LITILIADDRESS, LIZHENQIADDRESS,
LIWUSHUADDRESS, LILINGLIADDRESS, LIFANGYUADDRESS,
LISHENFAADDRESS, LIJIYUNADDRESS, LIJINQIANADDRESS}, //李忆如
{SHENJINGYANADDRESS, SHENTILIADDRESS, SHENZHENQIADDRESS,
SHENWUSHUADDRESS, SHENLINGLIADDRESS, SHENFANGYUADDRESS,
SHENSHENFAADDRESS, SHENJIYUNADDRESS, SHENJINQIANADDRESS}, //沈欺霜
{SUJINGYANADDRESS, SUTILIADDRESS, SUZHENQIADDRESS,
SUWUSHUADDRESS, SULINGLIADDRESS, SUFANGYUADDRESS,
SUSHENFAADDRESS, SUJIYUNNADDRESS, SUJINQIANADDRESS}}; //苏媚
//4个主角姓名,元素下标正好对应组合框的内容所在列,如王小虎下标为0,在组合框中的列值也为0
//其它主角也是一样
CString strPerson[4]={"王小虎", "李忆如", "沈欺霜", "苏媚"};
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CMy2Dlg dialog
CMy2Dlg::CMy2Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CMy2Dlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CMy2Dlg)
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMy2Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMy2Dlg)
DDX_Control(pDX, IDC_SCROLLBAR_FY, m_SBFY);
DDX_Control(pDX, IDC_SCROLLBAR_JQ, m_SBJQ);
DDX_Control(pDX, IDC_SCROLLBAR_LL, m_SBLL);
DDX_Control(pDX, IDC_SCROLLBAR_JY, m_SBJY);
DDX_Control(pDX, IDC_SCROLLBAR_SF, m_SBSF);
DDX_Control(pDX, IDC_SCROLLBAR_WS, m_SBWS);
DDX_Control(pDX, IDC_SCROLLBARZQ, m_SBZQ);
DDX_Control(pDX, IDC_SCROLLBARTL, m_SBTL);
DDX_Control(pDX, IDC_SCROLLBARJL, m_SBJL);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CMy2Dlg, CDialog)
//{{AFX_MSG_MAP(CMy2Dlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTONMODIFY, OnButtonModify)
ON_WM_HSCROLL()
ON_BN_CLICKED(IDC_BUTTONABOUT, OnButtonAbout)
ON_BN_CLICKED(IDC_BUTTON_ALLFULL, OnButtonAllFull)
ON_BN_CLICKED(IDC_BUTTON_ALLZERO, OnButtonAllZero)
ON_CBN_SELCHANGE(IDC_COMBORENWU, OnSelchangeComboRenWu)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CMy2Dlg message handlers
/*
**======================================
** OnInitDialog
** 初始化控件数组及资源数组
**======================================
*/
BOOL CMy2Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
//检查是否多开,禁止多开
HWND hwnd=::FindWindow(NULL, "仙剑2内存修改器 v1.0");
SetWindowText("我要多开");
//初始化滚动条控件指针数组,为了实现用循环读取或改变滚动条全满或全0等操作
//消除代码冗余,因为难以想像不用循环而是一句句的读写控件的属性值是一种什么情况
//SB为滚动条控件前缀,JL 、TL、等是属性值拼音的第一个字母,后面的类似
m_SBArray[0] = &m_SBJL;
m_SBArray[1] = &m_SBTL;
m_SBArray[2] = &m_SBZQ;
m_SBArray[3] = &m_SBWS;
m_SBArray[4] = &m_SBLL;
m_SBArray[5] = &m_SBFY;
m_SBArray[6] = &m_SBSF;
m_SBArray[7] = &m_SBJY;
m_SBArray[8] = &m_SBJQ;
//初始化当前属性值静态控件数组,实现用循环读写静态控件上的值
//消除代码冗余
HandleStaticCurrent[0] = IDC_STATIC_CURRENTLJ;
HandleStaticCurrent[1] = IDC_STATIC_CURRENTTL;
HandleStaticCurrent[2] = IDC_STATIC_CURRENTZQ;
HandleStaticCurrent[3] = IDC_STATIC_CURRENT_WS;
HandleStaticCurrent[4] = IDC_STATIC_CURRENT_LL;
HandleStaticCurrent[5] = IDC_STATIC_CURRENT_FY;
HandleStaticCurrent[6] = IDC_STATIC_CURRENT_SF;
HandleStaticCurrent[7] = IDC_STATIC_CURRENT_JY;
HandleStaticCurrent[8] = IDC_STATIC_CURRENT_JQ;
//初始化增加属性值静态控件数组,实现用循环读写增加值静态控件上的值
//消除代码冗余
HandleStaticAdd[0] = IDC_STATICJL;
HandleStaticAdd[1] = IDC_STATICTL;
HandleStaticAdd[2] = IDC_STATICZQ;
HandleStaticAdd[3] = IDC_STATIC_WS;
HandleStaticAdd[4] = IDC_STATIC_LL;
HandleStaticAdd[5] = IDC_STATIC_FY;
HandleStaticAdd[6] = IDC_STATIC_SF;
HandleStaticAdd[7] = IDC_STATIC_JY;
HandleStaticAdd[8] = IDC_STATIC_JQ;
//初始化里程ID为0
hProcess = 0;
//初始化游戏运行状态为没有启动
IfGameRun = false;
//将9个滚动条控件值范围设为0到10w,如果不用数组管理控件,这里要用9行同样的代码
//来设置这个范围,而且后面涉及到类似的操作都得用大量重复的语句来实现,这就是用数组
//的好处了
for(int i = 0; i < 9; i++)
{
m_SBArray[i]->SetScrollRange(0, 100000, true);
}
//我们要修改的游戏为 仙剑2,这就里为仙剑2 游戏程序名
//这里我不是通过 窗口标题来获取进程ID的,因而要遍历全部进程直到找到仙剑2进程为止
strFileName = "Pal2.exe";
CString strTemp;
//初始化组合框控件显示内容为 王小虎
((CComboBox*) GetDlgItem(IDC_COMBORENWU))->SetCurSel(0);
//这个变量为 组合框内容所在列,如 王小虎所在列为 0,往下加1
num = 0;
//如果游戏没有启动,则不设置相应控件上的值,而使用各自的默认值
if(!InitProcess())
{
return false;
}
//从游戏内存中读取4个人物的属性值
ReadMemory();
//将读取到的属性值显示到控件上
ReflashStatic();
//关闭打开的进程
MyCloseHandle();
return TRUE; // return TRUE unless you set the focus to a control
}
void CMy2Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CMy2Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMy2Dlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
/*
**======================================
** GetProcessIDFromProcessName
** 根据进程名(程序名)遍历进程获取进程ID
**======================================
*/
DWORD CMy2Dlg::GetProcessIDFromProcessName(CString strProcessName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
{
return 0;
}
PROCESSENTRY32 pInfo;
pInfo.dwSize = sizeof(PROCESSENTRY32);
//遍历进程直到找到 Pal2.exe为止
for (BOOL bRet = Process32First(hSnapshot, &pInfo); bRet; bRet = Process32Next(hSnapshot, &pInfo))
{
if(strcmp(strProcessName.GetBuffer(0), pInfo.szExeFile) == 0)
{
CloseHandle(hSnapshot);
return pInfo.th32ProcessID;
}
}
return 0;
}
/*
**======================================
** OnButtonModify
** 增加属性值事件处理函数
**======================================
*/
void CMy2Dlg::OnButtonModify()
{
// TODO: Add your control notification handler code here
if(!InitProcess())
{
//游戏已关闭,将各个控件值清一下
ClearExitGame();
//设置游戏状态为没有启动
IfGameRun = false;
MessageBox("请先运行游戏!");
return;
}
else IfGameRun = true;
//读取当前选择的主角的9个属性值
ReadMemory();
//将滚动条的值加上现在人物的属性值后写入属性值空间
WriteMemory();
//同时将刷新一下当前人物属性值,即当前属性值对应的静态控件
ReflashStatic();
//关闭进程
MyCloseHandle();
}
/*
**======================================
** OnHScroll
** 移动滚动条时触发此函数
**======================================
*/
void CMy2Dlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
//如果游戏没有启动,拖动滚动条时不作处理
if (IfGameRun == false)
{
return;
}
switch (nSBCode)
{
case SB_THUMBTRACK: //直接拖动滚动条时
pScrollBar->SetScrollPos(nPos);
break;
case SB_LINERIGHT: //鼠标点击滚动条右边的滚动箭头时
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() + 1000);
break;
case SB_RIGHT: //
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() + 1000);
break;
case SB_PAGERIGHT:// 鼠标点击滚动条右边的滚动栏时
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() + 10000);
break;
case SB_PAGELEFT: // 鼠标点击滚动条左边的滚动栏时
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() - 10000);
break;//4
case SB_LEFT: //
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() - 1000);
break;
case SB_LINELEFT: // 鼠标点击滚动条左边的滚动箭头时
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() - 1000);
break;
}
CString s;
//将滚动条位置值转换为字符串
s.Format("%d", pScrollBar->GetScrollPos());
//根据滚动条的值设置增加属性值静态控件上的值
for(int i = 0; i < 9; i++)
{
if(pScrollBar == m_SBArray[i])
{
GetDlgItem(HandleStaticAdd[i])->SetWindowText(s);
break;
}
}
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
/*
**======================================
** OnButtonAbout
** 弹出关于框
**======================================
*/
void CMy2Dlg::OnButtonAbout()
{
// TODO: Add your control notification handler code here
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
/*
**======================================
** OnButtonAllFull
** 设置滚动条值为满
**======================================
*/
void CMy2Dlg::OnButtonAllFull()
{
// TODO: Add your control notification handler code here
//如果游戏没有启动,不作处理
if (IfGameRun == false)
{
return;
}
//将9个滚动条控件值设为10w
for(int i = 0; i < 9; i++)
{
m_SBArray[i]->SetScrollPos(100000);
}
//将相应的9个静态控件设置为"100000"
for (i = 0; i < 9; i++)
{
GetDlgItem(HandleStaticAdd[i])->SetWindowText("100000");
}
}
/*
**======================================
** OnButtonAllZero
** 设置滚动条值为0
**======================================
*/
void CMy2Dlg::OnButtonAllZero()
{
// TODO: Add your control notification handler code here
//如果游戏没有启动,不作处理
if (IfGameRun == false)
{
return;
}
//将9个滚动条控件值清0
for(int i = 0; i < 9; i++)
{
m_SBArray[i]->SetScrollPos(0);
}
//将9个表示增加属性值的静态控件设置为"0"
for (i = 0; i < 9; i++)
{
GetDlgItem(HandleStaticAdd[i])->SetWindowText("0");
}
}
/*
**======================================
** OnSelchangeComboRenWu
** 下拉列表内容改变时触发此函数
**======================================
*/
void CMy2Dlg::OnSelchangeComboRenWu()
{
// TODO: Add your control notification handler code here
if(!InitProcess())
{
//因为游戏没有启动或已关闭,要将滚动条控件和静态控件上的值清除一下
ClearExitGame();
IfGameRun = false;
return;
}
else IfGameRun = true;
//读取内存中游戏人物属性
ReadMemory();
//将读取出来的值显示到当前属性值对应的静态控件上
ReflashStatic();
//关闭进程
MyCloseHandle();
}
/*
**======================================
** ReadMemory
** 读取游戏进程内存各个属性值
**======================================
*/
void CMy2Dlg::ReadMemory()
{
CString strTemp = "";
//取当前选择的人物,为了实现一次只改变一个人物的属性
((CComboBox*) GetDlgItem(IDC_COMBORENWU))->GetWindowText(strTemp);
for (int i = 0; i < 4; i++)
{
if (strTemp == strPerson[i])
{
//记录该人物在组合框中的列值,这个很重要,因为要根据列值
//来读取人物属性数组中的地址,来实现读或写属性值的操作
num = i;
break;
}
}
//读当前选择的人物的9个属性值
for (i = 0; i < 9; i++)
{
ReadProcessMemory(hProcess, (LPVOID) stPerson[num].dwShuXing[i], &DataShuXing[i], 4, NULL);
}
}
/*
**======================================
** WriteMemory
** 将新属性值写入游戏进程的指定内存单元
**======================================
*/
void CMy2Dlg::WriteMemory()
{
//将读取出来的属性值和滚动条的值相加后写入内存,改变人物属性
//人物各个属性地址见相应数组元素
for (int i = 0; i < 9; i++)
{
DataShuXing[i] += m_SBArray[i]->GetScrollPos();
WriteProcessMemory(hProcess, (LPVOID) stPerson[num].dwShuXing[i], &DataShuXing[i], 4, NULL);
}
}
/*
**======================================
** InitProcess
** 打开游戏进程并获取读写进程的权限
**======================================
*/
bool CMy2Dlg::InitProcess()
{
//传送游戏程序名来获取它的进程ID,这里仙剑2只有唯一进程
dwProcessID = GetProcessIDFromProcessName(strFileName);
//如果失败返回0
if(dwProcessID == 0)
{
return false;
}
//打开进程并得到读与权限
hProcess = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_TERMINATE |
PROCESS_VM_OPERATION | PROCESS_VM_READ |
PROCESS_VM_WRITE, FALSE, dwProcessID);
//打开进程失败返回0
if(hProcess == NULL)
{
return false;
}
return true;
}
/*
**======================================
** ReflashStatic
** 刷新一下各个静态控件上的文本
**======================================
*/
void CMy2Dlg::ReflashStatic()
{
//如果游戏没有启动不作操作
if (IfGameRun == false)
{
return;
}
CString strTemp[9];
//将9个属性值转换为字符串存储到数组,这9个属性值可以是刚从内存中读取出来的
//也可以是读取之后修改了的值
for (int i = 0; i < 9; i++)
{
strTemp[i].Format("%d", DataShuXing[i]);
}
//将属性值显示在相应控件上
for (i = 0; i < 9; i++)
{
GetDlgItem(HandleStaticCurrent[i])->SetWindowText(strTemp[i]);
}
}
/*
**======================================
** MyCloseHandle
** 关闭打开的进程
**======================================
*/
void CMy2Dlg::MyCloseHandle()
{
//关闭进程
CloseHandle(hProcess);
}
/*
**======================================
** ClearExitGame
** 当游戏退出时,要将人物当前属性值清0
**======================================
*/
void CMy2Dlg::ClearExitGame()
{
//当游戏已经退出时,将9个增加属性值静态控件上的值空
OnButtonAllZero();
//将9个当前属性静态控件上的值清空
for (int i = 0; i < 9; i++)
{
GetDlgItem(HandleStaticCurrent[i])->SetWindowText("0");
}
}
//定义人物各个属性地址宏
#define JINQIANADDRESS 0x0072D1E8
//王小虎属性地址
#define WANGJINGYANADDRESS 0x0072C2DC
#define WANGTILIADDRESS 0x0072C2E4
#define WANGZHENQIADDRESS 0x0072C2F0
#define WANGWUSHUADDRESS 0x0072C300
#define WANGLINGLIADDRESS 0x0072C308
#define WANGFANGYUADDRESS 0x0072C310
#define WANGSHENFAADDRESS 0x0072C318
#define WANGJIYUNADDRESS 0x0072C320
#define WANGJINQIANADDRESS JINQIANADDRESS
//李忆如属性地址
#define LIJINGYANADDRESS 0x0072CE94
#define LITILIADDRESS 0x0072CE9C
#define LIZHENQIADDRESS 0x0072CEA8
#define LIWUSHUADDRESS 0x0072CEB8
#define LILINGLIADDRESS 0x0072CEC0
#define LIFANGYUADDRESS 0x0072CEC8
#define LISHENFAADDRESS 0x0072CED0
#define LIJIYUNADDRESS 0x0072CED8
#define LIJINQIANADDRESS JINQIANADDRESS
//沈欺霜属性地址
#define SHENJINGYANADDRESS 0x0072C6C4
#define SHENTILIADDRESS 0x0072C6CC
#define SHENZHENQIADDRESS 0x0072C6D8
#define SHENWUSHUADDRESS 0x0072C6E8
#define SHENLINGLIADDRESS 0x0072C6F0
#define SHENFANGYUADDRESS 0x0072C6F8
#define SHENSHENFAADDRESS 0x0072C700
#define SHENJIYUNADDRESS 0x0072C708
#define SHENJINQIANADDRESS JINQIANADDRESS
//苏媚属性地址
#define SUJINGYANADDRESS 0x0072CAAC
#define SUTILIADDRESS 0x0072CAB4
#define SUZHENQIADDRESS 0x0072CAC0
#define SUWUSHUADDRESS 0x0072CAD0
#define SULINGLIADDRESS 0x0072CAD8
#define SUFANGYUADDRESS 0x0072CAE0
#define SUSHENFAADDRESS 0x0072CAE8
#define SUJIYUNNADDRESS 0x0072CAF0
#define SUJINQIANADDRESS JINQIANADDRESS
//定义人物数组结构体类型
struct _Person
{
DWORD dwShuXing[9];
};
//定义并初始化人物数组
//用数组存储4个主角的9个属性,可以使用循环来操作它们的值,大大的减少了代码冗余
const _Person stPerson[4] = {
{WANGJINGYANADDRESS, WANGTILIADDRESS, WANGZHENQIADDRESS,
WANGWUSHUADDRESS, WANGLINGLIADDRESS, WANGFANGYUADDRESS,
WANGSHENFAADDRESS, WANGJIYUNADDRESS, WANGJINQIANADDRESS}, //王小虎
{LIJINGYANADDRESS, LITILIADDRESS, LIZHENQIADDRESS,
LIWUSHUADDRESS, LILINGLIADDRESS, LIFANGYUADDRESS,
LISHENFAADDRESS, LIJIYUNADDRESS, LIJINQIANADDRESS}, //李忆如
{SHENJINGYANADDRESS, SHENTILIADDRESS, SHENZHENQIADDRESS,
SHENWUSHUADDRESS, SHENLINGLIADDRESS, SHENFANGYUADDRESS,
SHENSHENFAADDRESS, SHENJIYUNADDRESS, SHENJINQIANADDRESS}, //沈欺霜
{SUJINGYANADDRESS, SUTILIADDRESS, SUZHENQIADDRESS,
SUWUSHUADDRESS, SULINGLIADDRESS, SUFANGYUADDRESS,
SUSHENFAADDRESS, SUJIYUNNADDRESS, SUJINQIANADDRESS}}; //苏媚
//4个主角姓名,元素下标正好对应组合框的内容所在列,如王小虎下标为0,在组合框中的列值也为0
//其它主角也是一样
CString strPerson[4]={"王小虎", "李忆如", "沈欺霜", "苏媚"};
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CMy2Dlg dialog
CMy2Dlg::CMy2Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CMy2Dlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CMy2Dlg)
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMy2Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMy2Dlg)
DDX_Control(pDX, IDC_SCROLLBAR_FY, m_SBFY);
DDX_Control(pDX, IDC_SCROLLBAR_JQ, m_SBJQ);
DDX_Control(pDX, IDC_SCROLLBAR_LL, m_SBLL);
DDX_Control(pDX, IDC_SCROLLBAR_JY, m_SBJY);
DDX_Control(pDX, IDC_SCROLLBAR_SF, m_SBSF);
DDX_Control(pDX, IDC_SCROLLBAR_WS, m_SBWS);
DDX_Control(pDX, IDC_SCROLLBARZQ, m_SBZQ);
DDX_Control(pDX, IDC_SCROLLBARTL, m_SBTL);
DDX_Control(pDX, IDC_SCROLLBARJL, m_SBJL);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CMy2Dlg, CDialog)
//{{AFX_MSG_MAP(CMy2Dlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTONMODIFY, OnButtonModify)
ON_WM_HSCROLL()
ON_BN_CLICKED(IDC_BUTTONABOUT, OnButtonAbout)
ON_BN_CLICKED(IDC_BUTTON_ALLFULL, OnButtonAllFull)
ON_BN_CLICKED(IDC_BUTTON_ALLZERO, OnButtonAllZero)
ON_CBN_SELCHANGE(IDC_COMBORENWU, OnSelchangeComboRenWu)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CMy2Dlg message handlers
/*
**======================================
** OnInitDialog
** 初始化控件数组及资源数组
**======================================
*/
BOOL CMy2Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
//检查是否多开,禁止多开
HWND hwnd=::FindWindow(NULL, "仙剑2内存修改器 v1.0");
SetWindowText("我要多开");
//初始化滚动条控件指针数组,为了实现用循环读取或改变滚动条全满或全0等操作
//消除代码冗余,因为难以想像不用循环而是一句句的读写控件的属性值是一种什么情况
//SB为滚动条控件前缀,JL 、TL、等是属性值拼音的第一个字母,后面的类似
m_SBArray[0] = &m_SBJL;
m_SBArray[1] = &m_SBTL;
m_SBArray[2] = &m_SBZQ;
m_SBArray[3] = &m_SBWS;
m_SBArray[4] = &m_SBLL;
m_SBArray[5] = &m_SBFY;
m_SBArray[6] = &m_SBSF;
m_SBArray[7] = &m_SBJY;
m_SBArray[8] = &m_SBJQ;
//初始化当前属性值静态控件数组,实现用循环读写静态控件上的值
//消除代码冗余
HandleStaticCurrent[0] = IDC_STATIC_CURRENTLJ;
HandleStaticCurrent[1] = IDC_STATIC_CURRENTTL;
HandleStaticCurrent[2] = IDC_STATIC_CURRENTZQ;
HandleStaticCurrent[3] = IDC_STATIC_CURRENT_WS;
HandleStaticCurrent[4] = IDC_STATIC_CURRENT_LL;
HandleStaticCurrent[5] = IDC_STATIC_CURRENT_FY;
HandleStaticCurrent[6] = IDC_STATIC_CURRENT_SF;
HandleStaticCurrent[7] = IDC_STATIC_CURRENT_JY;
HandleStaticCurrent[8] = IDC_STATIC_CURRENT_JQ;
//初始化增加属性值静态控件数组,实现用循环读写增加值静态控件上的值
//消除代码冗余
HandleStaticAdd[0] = IDC_STATICJL;
HandleStaticAdd[1] = IDC_STATICTL;
HandleStaticAdd[2] = IDC_STATICZQ;
HandleStaticAdd[3] = IDC_STATIC_WS;
HandleStaticAdd[4] = IDC_STATIC_LL;
HandleStaticAdd[5] = IDC_STATIC_FY;
HandleStaticAdd[6] = IDC_STATIC_SF;
HandleStaticAdd[7] = IDC_STATIC_JY;
HandleStaticAdd[8] = IDC_STATIC_JQ;
//初始化里程ID为0
hProcess = 0;
//初始化游戏运行状态为没有启动
IfGameRun = false;
//将9个滚动条控件值范围设为0到10w,如果不用数组管理控件,这里要用9行同样的代码
//来设置这个范围,而且后面涉及到类似的操作都得用大量重复的语句来实现,这就是用数组
//的好处了
for(int i = 0; i < 9; i++)
{
m_SBArray[i]->SetScrollRange(0, 100000, true);
}
//我们要修改的游戏为 仙剑2,这就里为仙剑2 游戏程序名
//这里我不是通过 窗口标题来获取进程ID的,因而要遍历全部进程直到找到仙剑2进程为止
strFileName = "Pal2.exe";
CString strTemp;
//初始化组合框控件显示内容为 王小虎
((CComboBox*) GetDlgItem(IDC_COMBORENWU))->SetCurSel(0);
//这个变量为 组合框内容所在列,如 王小虎所在列为 0,往下加1
num = 0;
//如果游戏没有启动,则不设置相应控件上的值,而使用各自的默认值
if(!InitProcess())
{
return false;
}
//从游戏内存中读取4个人物的属性值
ReadMemory();
//将读取到的属性值显示到控件上
ReflashStatic();
//关闭打开的进程
MyCloseHandle();
return TRUE; // return TRUE unless you set the focus to a control
}
void CMy2Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CMy2Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMy2Dlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
/*
**======================================
** GetProcessIDFromProcessName
** 根据进程名(程序名)遍历进程获取进程ID
**======================================
*/
DWORD CMy2Dlg::GetProcessIDFromProcessName(CString strProcessName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
{
return 0;
}
PROCESSENTRY32 pInfo;
pInfo.dwSize = sizeof(PROCESSENTRY32);
//遍历进程直到找到 Pal2.exe为止
for (BOOL bRet = Process32First(hSnapshot, &pInfo); bRet; bRet = Process32Next(hSnapshot, &pInfo))
{
if(strcmp(strProcessName.GetBuffer(0), pInfo.szExeFile) == 0)
{
CloseHandle(hSnapshot);
return pInfo.th32ProcessID;
}
}
return 0;
}
/*
**======================================
** OnButtonModify
** 增加属性值事件处理函数
**======================================
*/
void CMy2Dlg::OnButtonModify()
{
// TODO: Add your control notification handler code here
if(!InitProcess())
{
//游戏已关闭,将各个控件值清一下
ClearExitGame();
//设置游戏状态为没有启动
IfGameRun = false;
MessageBox("请先运行游戏!");
return;
}
else IfGameRun = true;
//读取当前选择的主角的9个属性值
ReadMemory();
//将滚动条的值加上现在人物的属性值后写入属性值空间
WriteMemory();
//同时将刷新一下当前人物属性值,即当前属性值对应的静态控件
ReflashStatic();
//关闭进程
MyCloseHandle();
}
/*
**======================================
** OnHScroll
** 移动滚动条时触发此函数
**======================================
*/
void CMy2Dlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
//如果游戏没有启动,拖动滚动条时不作处理
if (IfGameRun == false)
{
return;
}
switch (nSBCode)
{
case SB_THUMBTRACK: //直接拖动滚动条时
pScrollBar->SetScrollPos(nPos);
break;
case SB_LINERIGHT: //鼠标点击滚动条右边的滚动箭头时
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() + 1000);
break;
case SB_RIGHT: //
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() + 1000);
break;
case SB_PAGERIGHT:// 鼠标点击滚动条右边的滚动栏时
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() + 10000);
break;
case SB_PAGELEFT: // 鼠标点击滚动条左边的滚动栏时
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() - 10000);
break;//4
case SB_LEFT: //
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() - 1000);
break;
case SB_LINELEFT: // 鼠标点击滚动条左边的滚动箭头时
pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() - 1000);
break;
}
CString s;
//将滚动条位置值转换为字符串
s.Format("%d", pScrollBar->GetScrollPos());
//根据滚动条的值设置增加属性值静态控件上的值
for(int i = 0; i < 9; i++)
{
if(pScrollBar == m_SBArray[i])
{
GetDlgItem(HandleStaticAdd[i])->SetWindowText(s);
break;
}
}
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
/*
**======================================
** OnButtonAbout
** 弹出关于框
**======================================
*/
void CMy2Dlg::OnButtonAbout()
{
// TODO: Add your control notification handler code here
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
/*
**======================================
** OnButtonAllFull
** 设置滚动条值为满
**======================================
*/
void CMy2Dlg::OnButtonAllFull()
{
// TODO: Add your control notification handler code here
//如果游戏没有启动,不作处理
if (IfGameRun == false)
{
return;
}
//将9个滚动条控件值设为10w
for(int i = 0; i < 9; i++)
{
m_SBArray[i]->SetScrollPos(100000);
}
//将相应的9个静态控件设置为"100000"
for (i = 0; i < 9; i++)
{
GetDlgItem(HandleStaticAdd[i])->SetWindowText("100000");
}
}
/*
**======================================
** OnButtonAllZero
** 设置滚动条值为0
**======================================
*/
void CMy2Dlg::OnButtonAllZero()
{
// TODO: Add your control notification handler code here
//如果游戏没有启动,不作处理
if (IfGameRun == false)
{
return;
}
//将9个滚动条控件值清0
for(int i = 0; i < 9; i++)
{
m_SBArray[i]->SetScrollPos(0);
}
//将9个表示增加属性值的静态控件设置为"0"
for (i = 0; i < 9; i++)
{
GetDlgItem(HandleStaticAdd[i])->SetWindowText("0");
}
}
/*
**======================================
** OnSelchangeComboRenWu
** 下拉列表内容改变时触发此函数
**======================================
*/
void CMy2Dlg::OnSelchangeComboRenWu()
{
// TODO: Add your control notification handler code here
if(!InitProcess())
{
//因为游戏没有启动或已关闭,要将滚动条控件和静态控件上的值清除一下
ClearExitGame();
IfGameRun = false;
return;
}
else IfGameRun = true;
//读取内存中游戏人物属性
ReadMemory();
//将读取出来的值显示到当前属性值对应的静态控件上
ReflashStatic();
//关闭进程
MyCloseHandle();
}
/*
**======================================
** ReadMemory
** 读取游戏进程内存各个属性值
**======================================
*/
void CMy2Dlg::ReadMemory()
{
CString strTemp = "";
//取当前选择的人物,为了实现一次只改变一个人物的属性
((CComboBox*) GetDlgItem(IDC_COMBORENWU))->GetWindowText(strTemp);
for (int i = 0; i < 4; i++)
{
if (strTemp == strPerson[i])
{
//记录该人物在组合框中的列值,这个很重要,因为要根据列值
//来读取人物属性数组中的地址,来实现读或写属性值的操作
num = i;
break;
}
}
//读当前选择的人物的9个属性值
for (i = 0; i < 9; i++)
{
ReadProcessMemory(hProcess, (LPVOID) stPerson[num].dwShuXing[i], &DataShuXing[i], 4, NULL);
}
}
/*
**======================================
** WriteMemory
** 将新属性值写入游戏进程的指定内存单元
**======================================
*/
void CMy2Dlg::WriteMemory()
{
//将读取出来的属性值和滚动条的值相加后写入内存,改变人物属性
//人物各个属性地址见相应数组元素
for (int i = 0; i < 9; i++)
{
DataShuXing[i] += m_SBArray[i]->GetScrollPos();
WriteProcessMemory(hProcess, (LPVOID) stPerson[num].dwShuXing[i], &DataShuXing[i], 4, NULL);
}
}
/*
**======================================
** InitProcess
** 打开游戏进程并获取读写进程的权限
**======================================
*/
bool CMy2Dlg::InitProcess()
{
//传送游戏程序名来获取它的进程ID,这里仙剑2只有唯一进程
dwProcessID = GetProcessIDFromProcessName(strFileName);
//如果失败返回0
if(dwProcessID == 0)
{
return false;
}
//打开进程并得到读与权限
hProcess = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_TERMINATE |
PROCESS_VM_OPERATION | PROCESS_VM_READ |
PROCESS_VM_WRITE, FALSE, dwProcessID);
//打开进程失败返回0
if(hProcess == NULL)
{
return false;
}
return true;
}
/*
**======================================
** ReflashStatic
** 刷新一下各个静态控件上的文本
**======================================
*/
void CMy2Dlg::ReflashStatic()
{
//如果游戏没有启动不作操作
if (IfGameRun == false)
{
return;
}
CString strTemp[9];
//将9个属性值转换为字符串存储到数组,这9个属性值可以是刚从内存中读取出来的
//也可以是读取之后修改了的值
for (int i = 0; i < 9; i++)
{
strTemp[i].Format("%d", DataShuXing[i]);
}
//将属性值显示在相应控件上
for (i = 0; i < 9; i++)
{
GetDlgItem(HandleStaticCurrent[i])->SetWindowText(strTemp[i]);
}
}
/*
**======================================
** MyCloseHandle
** 关闭打开的进程
**======================================
*/
void CMy2Dlg::MyCloseHandle()
{
//关闭进程
CloseHandle(hProcess);
}
/*
**======================================
** ClearExitGame
** 当游戏退出时,要将人物当前属性值清0
**======================================
*/
void CMy2Dlg::ClearExitGame()
{
//当游戏已经退出时,将9个增加属性值静态控件上的值空
OnButtonAllZero();
//将9个当前属性静态控件上的值清空
for (int i = 0; i < 9; i++)
{
GetDlgItem(HandleStaticCurrent[i])->SetWindowText("0");
}
}