CE程序设计


注:参考网上程序,和实验15大同小异


相关知识:

CE(Cheat Engine )是众多游戏修改工具的其中之一,也是一款开源软件。运行之后的界面,估计大多数人看见了都不知所谓,一对对数字描述了游戏的内存地址和对应的数值。不过对于普通用户来说,这些都不需要去了解和理解,只需要把游戏中想要修改的数值找到对应的地址,并修改成想要的数值就可以了。

寻找数值对应地址的方法一般是:运行1.CE->2.运行游戏->3.打开游戏进程->4.首次搜索一个数值->5.回游戏中让这个数值增加或减少 ->6.回CE按数值增减的情况再次搜索->7.重复5和6直到得到一个或很少的几个结果->8.在这几个结果中判断哪一个是真正的结果(一般来说,搜索几次之后,基本就剩下一个修改地址了)。

参考:https://baike.baidu.com/item/CE%E5%86%85%E5%AD%98%E4%BF%AE%E6%94%B9%E5%99%A8/9225930?fr=aladdin - CE内存修改器


代码实现:

内存查找类


#ifndef __MEMFINDER_H__
#define __MEMFINDER_H__
#define _CRT_SECURE_NO_WARNINGS#include <windows.h>class CMemFinder
{
public:CMemFinder(DWORD dwProcessId);virtual ~CMemFinder();// 属性
public:BOOL IsFirst() const { return m_bFirst; }BOOL IsValid() const { return m_hProcess != NULL; }int GetListCount() const { return m_nListCnt; }DWORD operator [](int nIndex) { return m_arList[nIndex]; }// 操作virtual BOOL FindFirst(DWORD dwValue);virtual BOOL FindNext(DWORD dwValue);virtual BOOL WriteMemory(DWORD dwAddr, DWORD dwValue);// 实现
protected:virtual BOOL CompareAPage(DWORD dwBaseAddr, DWORD dwValue);DWORD m_arList[1024];  // 地址列表int m_nListCnt;      // 有效地址的个数HANDLE m_hProcess;    // 目标进程句柄BOOL m_bFirst;     // 是不是第一次搜索
};->OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄。
CMemFinder::CMemFinder(DWORD dwProcessId)
{m_nListCnt = 0;m_bFirst = TRUE;m_hProcess = ::OpenProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION, FALSE, dwProcessId);
}CMemFinder::~CMemFinder()
{if(m_hProcess != NULL)::CloseHandle(m_hProcess);
}->从打开的进程开始查找查找的数据的地址
BOOL CMemFinder::FindFirst(DWORD dwValue)
{const DWORD dwOneGB = 1024*1024*1024; // 1GBconst DWORD dwOnePage = 4*1024;      // 4KBif(m_hProcess == NULL)return FALSE;// 查看操作系统类型,以决定开始地址DWORD dwBase;OSVERSIONINFO vi = { sizeof(vi) };::GetVersionEx(&vi);if (vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)dwBase = 4*1024*1024;     // Windows 98系列,4MB  elsedwBase = 640*1024;     // Windows NT系列,64KB// 在开始地址到2GB的地址空间进行查找for(; dwBase < 2*dwOneGB; dwBase += dwOnePage){// 比较1页大小的内存CompareAPage(dwBase, dwValue);}m_bFirst = FALSE;return TRUE;
}->比较一页内存(指定范围),查看值是否相等,相等保存到全局数组中
BOOL CMemFinder::CompareAPage(DWORD dwBaseAddr, DWORD dwValue)
{// 读取1页内存BYTE arBytes[4096];if(!::ReadProcessMemory(m_hProcess, (LPVOID)dwBaseAddr, arBytes, 4096, NULL))return FALSE; // 此页不可读// 在这1页内存中查找DWORD* pdw;for(int i=0; i<(int)4*1024-3; i++){pdw = (DWORD*)&arBytes[i];if(pdw[0] == dwValue)  // 等于要查找的值?{if(m_nListCnt >= 1024)return FALSE;// 添加到全局变量中m_arList[m_nListCnt++] = dwBaseAddr + i;}}return TRUE;
}->查找全局数组中下一个地址的数据
BOOL CMemFinder::FindNext(DWORD dwValue)
{// 保存m_arList数组中有效地址的个数,初始化新的m_nListCnt值int nOrgCnt = m_nListCnt;m_nListCnt = 0;  // 在m_arList数组记录的地址处查找BOOL bRet = FALSE;   // 假设失败 DWORD dwReadValue;for(int i=0; i<nOrgCnt; i++){if(::ReadProcessMemory(m_hProcess, (LPVOID)m_arList[i], &dwReadValue, sizeof(DWORD), NULL)){if(dwReadValue == dwValue){m_arList[m_nListCnt++] = m_arList[i];bRet = TRUE;}}}return bRet;
}->修改内存中的数据
BOOL CMemFinder::WriteMemory(DWORD dwAddr, DWORD dwValue)
{return ::WriteProcessMemory(m_hProcess, (LPVOID)dwAddr, &dwValue, sizeof(DWORD), NULL);
}// 下面的CMsgMemFinder类在搜索内存时还会不断处理消息// 在等待期间处理消息
void WaitForIdle()
{MSG msg;  while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){::TranslateMessage(&msg);::DispatchMessage(&msg);}
}class CMsgMemFinder : public CMemFinder
{
public:CMsgMemFinder(DWORD dwProcessId) : CMemFinder(dwProcessId){ m_bIsWorking = FALSE;}// 是否在工作BOOL IsWorking() const { return m_bIsWorking; }// 开始查找virtual BOOL FindFirst(DWORD dwValue){m_bIsWorking = TRUE;BOOL b = CMemFinder::FindFirst(dwValue);m_bIsWorking = FALSE;return b;}protected:// 比较1页内存前,先处理消息virtual BOOL CompareAPage(DWORD dwBaseAddr, DWORD dwValue){WaitForIdle();return CMemFinder::CompareAPage(dwBaseAddr, dwValue);}BOOL m_bIsWorking;
};#endif // __MEMFINDER_H__-----------------------------------------------各控件的作用---------------------------------------BOOL CMainDialog::OnInitDialog()
{CDialog::OnInitDialog();// 设置图标SetIcon(theApp.LoadIcon(IDI_MAIN), FALSE);m_comboList.SubclassWindow(::GetDlgItem(m_hWnd, IDC_PROLIST));m_btnUpdate.SubclassWindow(*GetDlgItem(IDC_UPDATE));m_listAddr.SubclassWindow(*GetDlgItem(IDC_ADDRLIST));m_pFinder = NULL;// 创建状态栏,设置它的属性(CStatusBarCtrl类封装了对状态栏控件的操作)m_bar.Create(WS_CHILD|WS_VISIBLE|SBS_SIZEGRIP, CRect(0, 0, 0, 0), this, 10);m_bar.SetBkColor(RGB(0xa6, 0xca, 0xf0));        // 背景色int arWidth[] = { 145, -1 };// 更新进程列表OnUpdate();// 更新界面UIControl();return FALSE;
}void CMainDialog::OnSearch()
{// 创建查找对象if(m_pFinder == NULL){DWORD dwId = m_comboList.GetItemData(m_comboList.GetCurSel());m_pFinder = new CMsgMemFinder(dwId);UIControl();}if(m_pFinder->IsWorking())return;CString sText;GetDlgItem(IDC_EDITSEARCH)->GetWindowText(sText);if(!sText.IsEmpty()){DWORD dwSearch = atoi(sText);if(m_pFinder->IsFirst()){m_bar.SetText("正在搜索,请耐心等待...", 0, 0);  // 长时间操作m_pFinder->FindFirst(dwSearch);}else{m_pFinder->FindNext(dwSearch);   }sText.Format("搜索到%d个结果", m_pFinder->GetListCount());m_bar.SetText(sText, 0, 0);}else{if(m_pFinder->IsValid()){m_bar.SetText("打开目标进程成功!", 0, 0);}else{m_bar.SetText("打开目标进程失败!", 0, 0);}}UIControl();
}void CMainDialog::OnRestart()
{if(m_pFinder != NULL){if(m_pFinder->IsWorking())return;delete m_pFinder;m_pFinder = NULL;}m_bar.SetText("空闲", 0, 0);UIControl();
}void CMainDialog::UIControl()
{m_comboList.EnableWindow(m_pFinder == NULL);m_btnUpdate.EnableWindow(m_pFinder == NULL);GetDlgItem(IDC_MODIFY)->EnableWindow(m_pFinder != NULL);// 列出搜索结果if(m_pFinder != NULL){CString s;m_listAddr.ResetContent();for(int i=0; i<m_pFinder->GetListCount(); i++){s.Format("%08lX", (*m_pFinder)[i]);m_listAddr.AddString(s);}}
}void CMainDialog::OnUpdate()
{// 删除所有的项m_comboList.ResetContent();int nItem = 0;    // 项计数PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) }; HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if(hProcessSnap == INVALID_HANDLE_VALUE) return; if(Process32First(hProcessSnap, &pe32)) { do {  CString szText;szText.Format("%s  (%d)", pe32.szExeFile, pe32.th32ProcessID);m_comboList.InsertString(nItem, szText);m_comboList.SetItemData(nItem, pe32.th32ProcessID);nItem++;} while(Process32Next(hProcessSnap, &pe32)); }::CloseHandle(hProcessSnap);m_comboList.SetCurSel(nItem-1);
}void CMainDialog::OnModify()
{CString sText;DWORD dwValue;GetDlgItem(IDC_EDITMODIFY)->GetWindowText(sText);if(sText.IsEmpty()){MessageBox("请输入一个你想要的值!");return;}dwValue = (DWORD)atoi(sText);GetDlgItem(IDC_SELADDR)->GetWindowText(sText);if(sText.IsEmpty()){MessageBox("请输入你要将哪个地址的值改为:%s", sText);return;}DWORD dwAddr;sscanf(sText, "%x", &dwAddr);if(m_pFinder != NULL){if(m_pFinder->WriteMemory(dwAddr, dwValue))m_bar.SetText("修改成功!", 0, 0);elsem_bar.SetText("修改失败!", 0, 0);}
}void CMainDialog::OnSelectChange()
{CString sText;int nSel = m_listAddr.GetCurSel();m_listAddr.GetText(nSel, sText);GetDlgItem(IDC_SELADDR)->SetWindowText(sText);
}void CMainDialog::OnCancel()
{OnRestart();CDialog::OnCancel();
}


简单的说一下:

  1. 这个工具的实现原理就是通过 CreateToolhelp32Snapshot 获取系统中所有进程中的句柄。

PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };

HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

注:CreateToolhelp32Snapshot可以通过获取进程信息为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程建立一个快照。说到底,可以获取系统中正在运行的进程信息,线程信息,等。

2.通过选择进程,打开指定进程的句柄:使用

CMemFinder::CMemFinder(DWORD dwProcessId)

{

m_nListCnt = 0;

m_bFirst = TRUE;

m_hProcess = ::OpenProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION, FALSE, dwProcessId);

}

  1. 然后通过修改进程中的值,再查找修改值在进程的地址(存放的是等于该值的所有地址)逐步过滤到只剩下一个地址。

BOOL CMemFinder::CompareAPage(DWORD dwBaseAddr, DWORD dwValue)

{

// 读取1页内存

BYTE arBytes[4096];

if(!::ReadProcessMemory(m_hProcess, (LPVOID)dwBaseAddr, arBytes, 4096, NULL))

return FALSE; // 此页不可读

// 在这1页内存中查找

DWORD* pdw;

for(int i=0; i<(int)4*1024-3; i++)

{

pdw = (DWORD*)&arBytes[i];

if(pdw[0] == dwValue) // 等于要查找的值?

{

if(m_nListCnt >= 1024)

return FALSE;

// 添加到全局变量中

m_arList[m_nListCnt++] = dwBaseAddr + i;

}

}

return TRUE;

}

4.然后写入值修改该地址存放的数据。

BOOL CMemFinder::WriteMemory(DWORD dwAddr, DWORD dwValue)

{

return ::WriteProcessMemory(m_hProcess, (LPVOID)dwAddr, &dwValue, sizeof(DWORD), NULL);

}

内存修改成功!!!


此次试验与实验15游戏内存修改原理基本相同,实验15的内存修改步骤大致如下:

获得窗口句柄->获得该窗口的进程(在内存中)->打开进程->修改内存

使用的函数分别有:

FindWindow(NULL, L"shooting")->GetSafeHwnd();

函数功能:该函数获得一个顶层窗口的句柄,该窗口的类名和窗口名与给定的字符串相匹配。这个函数不查找子窗口。在查找时不区分大小写。

GetWindowThreadProcessId(hWnd, &Pid);

函数功能:GetWindowThreadProcessId是一种计算机函数,功能是找出某个窗口的创建者(线程或进程),返回创建者的标志符,函数原型是DWORD GetWindowThreadProcessId。

hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);

函数功能:OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄。

result = WriteProcessMemory(hProcess, (LPVOID)Address, &Score, 4, 0);

函数功能:WriteProcessMemory是计算机语言中的一种函数。此函数能写入某一进程的内存区域(直接写入会出Access Violation错误),故需此函数入口区必须可以访问,否则操作将失败。

而此次试验是在实验15基础上进行改进,它不是使用获取窗口句柄打开进程,而是通过获取系统中的所有进程句柄,找到运行的游戏,这个其实改进了一下,但使用窗口句柄代开进程也未尝不可。

而最大的不同,且最重要的点是:它通过比较进程中的所有地址中的数据,通过游戏的数值变化,使用该数值对内存中的进行过滤(保存地址,逐步减少选中的地址数量),最后找到想要修改的地址,方法高明了不止一筹。

实验步骤为:

获取系统所有进程句柄 -> 打开指定进程 -> 观察游戏值的变化输入值比较内存中所有地址的数据,筛选出相等数据的地址,重复操作过滤到只剩一个地址 -> 修改内存


通过参考网上资料,进行此次实验,学会了CE动态内存修改器的制作,也算是对这个学期网络安全课程的一个总结。通过这学期网络安全的学习,对网络有了更深一步的了解,也对自己以后从事IT行业打下了一定安全基础。我感觉这门课是大学为数不多真正学到东西的一门课。老师讲课条理清楚,而且结合他生活中的例子,把我们带进了神秘未知的安全世界,网络安全是程序员捍卫自己地位的权杖。俗话说师傅领进门,修行在个人,想要真正成为网络安全的专家也不仅仅是通过十多节课就可以达到,更需要自己去专研,但姜晔老师把我们从门外汉领进了安全的大门,我们以后要进行网络安全的深入学习可以是少了很多拦路石,少走了不知道多少弯路。

感谢老师一学期的教导~

致谢!

网络安全学习第16篇 - CE游戏动态内存修改器相关推荐

  1. 手机html5游戏内存修改,一种防止游戏被内存修改器修改的方法

    一种防止游戏被内存修改器修改的方法 [技术领域] [0001]本发明涉及游戏数据处理领域,尤其涉及一种防止游戏被内存修改器修改的方法. [背景技术] [0002]目前,电子游戏越来越受到大家的喜爱,丰 ...

  2. 网络安全学习第15篇 - 游戏内存修改

    请依据实验文档<游戏辅助的实现>中的内容,自行编写一个游戏内存数据修改程序,使其可以对某款游戏的某一项或某几项数值进行修改.有余力的同学可以任选一款游戏尝试修改. 实验报告的最后请简述,作 ...

  3. Java外挂开发之内存修改器(类似CE)

    最近闲着蛋疼,无聊之中用CE耍了一哈游戏,发现挺好用的,于是就想,我自己能不能写个内存修改器呢?于是乎,这个软件就诞生了! 当然我只会Java,C++嘛,了解了解,嘿嘿,所以这个工具也是用Java写的 ...

  4. 服务器数据库不能修改器,游戏服务器数据库修改器

    游戏服务器数据库修改器 内容精选 换一换 如果已添加的数据库服务器的用户名和密码已修改或者访问数据库的用户名和密码配置有误,您可以参考本章节进行重新配置.已获取管理控制台的登录帐号与密码.已完成数据库 ...

  5. 攻击游戏服务器 修改数据库,游戏服务器数据库修改器

    游戏服务器数据库修改器 内容精选 换一换 应用容器化改造有三种方式,您可单击这里查看.本教程以某游戏为例,将该游戏进行微服务的架构改造,再进行容器化.本教程不对改造细节做深度讲解,仅讲解大致的建议.如 ...

  6. 【术】游戏内存修改器原理及游戏敏感数值加密建议

    介绍市面上主流游戏内存修改器的原理,对症下药,让游戏从数据加密层面上更好的抵御这类内存修改器. 主流游戏内存修改器有三大内存搜索功能: 1. 基础数值搜索 2. 模糊搜索 3. 反加密搜索 基础数值搜 ...

  7. 内存修改器对游戏的危害

    内存修改器是游戏外挂里面的一个大类,通过修改器可实现很多魔幻且具有很大破坏性的功能. 下面几张图是使用修改器实现的游戏破解效果. 内存修改器实现的透视功能 内存修改器实现的遁地功能 修改器实现的飞天功 ...

  8. ce游戏内存修改器(Cheat Engine)

    ce修改器(Cheat Engine)一款专门修改内存修改编辑的游戏工具它包括16进制编辑,反汇编程序,内存查找工具 新版6.1 版的CE与6.0 最大的区别就是添加了修改器制作工具,比之前 5.6. ...

  9. C++ Primer Plus学习(十一)——类和动态内存分配

    类和动态内存分配 动态内存和类 静态类成员 特殊成员函数 string类的改进 构造函数中的new 返回对象 指向对象的指针 成员初始化列表(member initializer list) 动态内存 ...

  10. 使用java来写一个游戏外挂-内存修改程序(辅助-开篇)

    很久以前研究过如何使用java写一个内存读写的程序,但是java都是知道的 它是在虚拟机上进行运行的,所以没办法进行内存的读写 所以用到了jan来执行windows自带的 kernel32.dll程序 ...

最新文章

  1. 【爬虫、算法】基于Dijkstra算法的武汉地铁路径规划!
  2. LintCode-375.克隆二叉树
  3. CAS单点登录原理简单介绍
  4. 11、java中的I/O流(1)
  5. git仓库创建后,由主支变成开发分支
  6. 母版页的铵钮事件去获取子页的内容
  7. 洛谷——P1428 小鱼比可爱
  8. Web前端开发需要掌握的技术有哪些?
  9. VC dimension(Vapnik-Chervonenkis dimension)
  10. 计算机主机时间不保存,电脑主板系统时间不能保存
  11. 倾斜摄影——三维建模软件ContextCapture (简体中文版安装教程-附软件下载)
  12. 关于DoEvents
  13. 读书笔记:《软件架构师应该知道的97件事》
  14. 对项目经理而言,PMP认证是否对职业生涯的发展有帮助?
  15. 001 线性代数之行列式:定义、逆序数、余子式与代数余子式、n个易算行列式、范德蒙行列式
  16. java中二维数组的长度
  17. c++: Logger日志信息
  18. HTML+CSS+前端项目
  19. 奥密克戎如洪水猛兽 美国财政政策蒙上担忧的阴影
  20. 2018年找工作的经验总结

热门文章

  1. abaqus 不收敛问题
  2. 计算机网络(谢希仁)第六版课件
  3. .Net C# 微信APP支付的开发步骤
  4. CentOS故障排除详解(2): 进程相关
  5. 【Ubuntu】USB 摄像头测试工具
  6. php解析视频_YY神曲视频PHP解析调用代码
  7. (学习笔记)图像处理——同态滤波
  8. 计算机组成原理-检错码、纠错码
  9. 二叉树的非递归遍历算法
  10. 需求与商业模式分析-1-商业模式画布