最近闲着蛋疼,无聊之中用CE耍了一哈游戏,发现挺好用的,于是就想,我自己能不能写个内存修改器呢?于是乎,这个软件就诞生了!
当然我只会Java,C++嘛,了解了解,嘿嘿,所以这个工具也是用Java写的
这个工具在写内存搜索时,简直累死了,最开始暴力搜索,效率慢的要死,经过三天的不懈努力,终于搞定这搜索功能!这也是整个工具最难的部分,至少我是这么认为的
微软的MSDN都被我翻了又翻 - - !
下载解压后的目录结构:

第一个是源代码:用Eclipse导入即可,记得修改Build Path
第二个是打包好的Jar,如果电脑安装了Java,就可以直接运行
第三个是打包好的exe安装程序,可以在没有安装Java的电脑上运行
不多说了,先上一张效果图: (这里用植物大战僵尸做实验,4399的小游戏)
原来阳光是150,被我改成1024了

先说说这个软件的使用步骤吧,与CE用法差不多,先打开游戏进程,输入搜索的游戏值(比如这里改阳光,所以输入150),然后开始搜索,搜索完成后,种一柱植物,或者捡一个阳光,总之让阳光值发生变化,然后再点击搜索变化,地址就出来啦,最后,鼠标点击,输入修改值,点击写入内存就可以咯
如果搜索变化没有,就多搜两次哦
本工具开发环境如下:
开发工具:Eclipse
开发语言:Java
开发系统:windows7
JDK版本:1.6
项目所需的第三方包:
官网下载JNA包:https:///java-native-access/jna 这个包用于调用windows系统函数库
官网下载皮肤包:http://www.jtattoo.net/ 这个包是Swing的皮肤包,用于做界面用的
代码实现大致思路如下:
获取Debug权限->创建系统快照->获取进程ID->获取进程在内存中的首地址与结束地址->打开进程->遍历内存->查找数据->修改数据
项目包目录如下:

entity 这是实体包
event 这是窗体的事件监听相应包
impl 功能的核心实现
interfaces C++ API函数接口定义
quantity C++ API函数常量描述
structure C++结构体描述
wnd 软件界面
下面开始给出关键代码:
代码一:获取Debug权限,这里可以理解为赋予软件管理员,如果不获取Debug权限,会导致有些进程可能会拒绝访问
/**
* processHandle 需要给予的进程句柄
* privilegeValue 特权值,详情请参阅LookupPrivilegeValue接口
* **/
public ExecuteResult give(int processHandle,String privilegeValue)
{
ExecuteResult executeResult = new ExecuteResult();
//创建令牌句柄指針,用于保存OpenProcessToken函数返回的令牌
HANDLEByReference tokenHandle = new HANDLEByReference();
try
{
//打开进程令牌,用于查询和修改令牌
if(Advapi32_DLL.INSTANCE.OpenProcessToken(processHandle, OpenProcessToken.TOKEN_ADJUST_PRIVILEGES|OpenProcessToken.TOKEN_QUERY, tokenHandle))
{
//创建一个令牌特权,初始化为1,用于保存LookupPrivilegeValue函数返回的令牌特权
TOKEN_PRIVILEGES tkp = new TOKEN_PRIVILEGES(1);
//初始化令牌特LUID值
tkp.Privileges[0] = new LUID_AND_ATTRIBUTES();
tkp.Privileges[0].Luid = new LUID();
tkp.Privileges[0].Attributes = OpenProcessToken.SE_PRIVILEGE_ENABLED;
//查看系统权限的特权值,返回到tkp LUID
if(Advapi32_DLL.INSTANCE.LookupPrivilegeValue(null, privilegeValue, tkp.Privileges[0].Luid))
{
//告诉系统启用该令牌
Advapi32_DLL.INSTANCE.AdjustTokenPrivileges(tokenHandle.getValue(), false, tkp, tkp.size(), null, null);
}
}
}
finally
{
//释放令牌指针
ReferenceFree.free(tokenHandle);
//获取执行结果
executeResult.setLastError(Kernel32_DLL.INSTANCE.GetLastError());
//释放句柄资源
Kernel32_DLL.INSTANCE.CloseHandle(processHandle);
}
return executeResult;
}
代码二:创建系统快照,获取系统进程相关信息
/**
* 得到系统进程列表
* */
public ExecuteResult getProcess()
{
ExecuteResult executeResult = new ExecuteResult();
//获取结果集
List<Process> list = new ArrayList<Process>();
//创建当前系统进程快照,返回快照句柄,具体参考com.memory.interfaces.Kernel32_DLL中的描述
int processHandle = Kernel32_DLL.INSTANCE.CreateToolhelp32Snapshot(CreateToolhelp32Snapshot.TH32CS_SNAPPROCESS, 0);
//快照結果
int lastError = Kernel32_DLL.INSTANCE.GetLastError();
if(processHandle==0 || lastError!=0)
{
executeResult.setLastError(lastError);
executeResult.setMessage("获取系统进程信息失败,错误代码:"+lastError);
return executeResult;
}
try
{
//创建进程结构体,用于保存进程的相关信息,具体参考com.memory.entity.Process中的描述
PROCESSENTRY32 lppe = new PROCESSENTRY32();
//根据快照句柄遍历系统进程
while(Kernel32_DLL.INSTANCE.Process32Next(processHandle, lppe))
{
Process temp = new Process();
temp.setProcessName(lppe.getSzExeFileStr());
temp.setPid(lppe.th32ProcessID);
list.add(temp);
}
if(list.size()!=0)
{
executeResult.setValue(list);
}
else
{
lastError = Kernel32_DLL.INSTANCE.GetLastError();
executeResult.setLastError(lastError);
executeResult.setMessage("获取系统进程信息失败,错误代码:"+lastError);
}
}
finally
{
//释放句柄资源
Kernel32_DLL.INSTANCE.CloseHandle(processHandle);
}
return executeResult;
}
代码三: 获取进程的开始内存地址与结束内存地址,获取系统的内存地址
/**
* 查询进程在内存中的开始地址与结束地址
* **/
public ExecuteResult queryProcessRange(int pid)
{
ExecuteResult executeResult = new ExecuteResult();
//创建内存范围对象
MemoryRange range = new MemoryRange();
//创建进程模版快照,查询应用程序的在内存中的基地址
int handleModule = Kernel32_DLL.INSTANCE.CreateToolhelp32Snapshot(CreateToolhelp32Snapshot.TH32CS_SNAPMODULE, pid);
//快照执行结果
int lastError = Kernel32_DLL.INSTANCE.GetLastError();
executeResult.setLastError(lastError);
//判断结果
if(lastError==5)
{
executeResult.setMessage("无法打开进程,系统Debug权限获取失败,请以管理员方式重新运行程序!");
return executeResult;
}
//如果为299,说明只有部分权限,判断该进程是否是64位进程
else if(lastError==299)
{
//声明INT指针,保存IsWow64Process返回的值
IntByReference Wow64Process = new IntByReference();
int handle = Kernel32_DLL.INSTANCE.OpenProcess(OpenProcess.PROCESS_ALL_ACCESS, false, pid);
if(Kernel32_DLL.INSTANCE.IsWow64Process(handle, Wow64Process))
{
//如果为64位进程,那么久获取系统的内存范围
if(Wow64Process.getValue()==0)
{
executeResult = querySystemRange();
}
}
else
{
executeResult.setMessage("无法打开该进程,错误代码:"+lastError);
}
//释放内存
ReferenceFree.free(Wow64Process);
Kernel32_DLL.INSTANCE.CloseHandle(handle);
return executeResult;
}
else if(lastError!=0)
{
executeResult.setMessage("无法打开该进程,OpenProcess函数返回错误码:"+lastError);
return executeResult;
}
try
{
MODULEENTRY32 lpme = new MODULEENTRY32();
if(Kernel32_DLL.INSTANCE.Module32First(handleModule, lpme))
{
range.setMinValue(lpme.modBaseAddr);
if(Kernel32_DLL.INSTANCE.Module32Next(handleModule, lpme))
{
range.setMaxValue(lpme.modBaseAddr);
}
}
//执行结果返回值
executeResult.setValue(range);
//执行结果
lastError = Kernel32_DLL.INSTANCE.GetLastError();
if(range.getMinValue() == 0 && lastError!=0)
{
executeResult.setLastError(lastError);
executeResult.setMessage("Module32Next失败,错误代码:"+lastError);
}
}
finally
{
//释放快照
Kernel32_DLL.INSTANCE.CloseHandle(handleModule);
}
return executeResult;
}
/**
* 查询当前系统的可搜索的开始地址与结束地址
* **/
public ExecuteResult querySystemRange()
{
ExecuteResult executeResult = new ExecuteResult();
//创建内存范围对象
MemoryRange range = new MemoryRange();
//创建描述系统信息的结构
SYSTEM_INFO info = new SYSTEM_INFO();
//获取系统内存范围
Kernel32_DLL.INSTANCE.GetSystemInfo(info);
range.setMinValue(Pointer.nativeValue(info.lpMinimumApplicationAddress));
range.setMaxValue(Pointer.nativeValue(info.lpMaximumApplicationAddress));
//返回值
executeResult.setValue(range);
//调用结果
int lastError = Kernel32_DLL.INSTANCE.GetLastError();
if(lastError!=0)
{
executeResult.setLastError(lastError);
executeResult.setMessage("获取系统内存地址范围失败,错误代码:"+lastError);
}
return executeResult;
}
代码四:搜索内存,也是最核心的部分,个人感觉也是最难的部分,可能是之前没做个之类的软件吧,反正耗费我三天,头都要炸了
代码五:内存的写实现
package com.memory.impl;
import com.memory.entity.ExecuteResult;
import com.memory.interfaces.Kernel32_DLL;
import com.memory.quantity.OpenProcess;
import com.memory.structure.MEMORY_BASIC_INFORMATION;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
/**
* 内存的写实现类
* 作者:Code菜鸟
* */
public class MemoryWrite
{
/**
* 写入内存实现方法
* pid 进程ID
* lpBaseAddress 写入地址
* value 写入值
* dataType 数据类型,这个值确定value的实际数据类型
* **/
public ExecuteResult write(int pid,int lpBaseAddress,String value,int dataType)
{
ExecuteResult result = new ExecuteResult();
int hProcess = Kernel32_DLL.INSTANCE.OpenProcess(OpenProcess.PROCESS_ALL_ACCESS, false, pid);
//判断进程句柄是否打开成功
int lastError = Kernel32_DLL.INSTANCE.GetLastError();
result.setLastError(lastError);
if(lastError==5)
{
result.setMessage("进程拒绝访问,可能是系统Debug权限获取失败,请以管理员方式重新运行程序!");
return result;
}
else if(lastError!=0)
{
result.setMessage("无法打开该进程,错误代码:"+lastError);
return result;
}
try
{
//判断内存地址是否合法幷且是否满足读写权限
MEMORY_BASIC_INFORMATION lpBuffer = new MEMORY_BASIC_INFORMATION();
Kernel32_DLL.INSTANCE.VirtualQueryEx(hProcess, lpBaseAddress, lpBuffer, lpBuffer.size());
if(!(lpBuffer.state == MEMORY_BASIC_INFORMATION.MEM_COMMIT
&& lpBuffer.protect == MEMORY_BASIC_INFORMATION.PAGE_READWRITE))
{
result.setLastError(-1);
result.setMessage("内存地址不存在或者该内存无法读写!");
return result;
}
//新内存地址,用于写入内存用
Pointer updatePointer = null;
int size = 4;
switch(dataType)
{
//整形int
case 0:
size = 4;
updatePointer = new Memory(size);
updatePointer.setInt(0, Integer.parseInt(value));
break;
//短整形short
case 1:
size = 2;
updatePointer = new Memory(size);
updatePointer.setShort(0, Short.parseShort(value));
break;
//长整形Long
case 2:
size = 8;
updatePointer = new Memory(size);
updatePointer.setLong(0, Long.parseLong(value));
break;
//单精度浮点 float
case 3:
size = 4;
updatePointer = new Memory(size);
updatePointer.setFloat(0, Float.parseFloat(value));
break;
//双精度浮点 double
case 4:
size = 8;
updatePointer = new Memory(size);
updatePointer.setDouble(0, Double.parseDouble(value));
break;
//字节byte
case 5:
size = 1;
updatePointer = new Memory(size);
updatePointer.setByte(0, Byte.parseByte(value));
break;
}
//写入内存
boolean writeResult = Kernel32_DLL.INSTANCE.WriteProcessMemory(hProcess, lpBaseAddress, updatePointer, size, 0);
//是否写入成功
lastError = Kernel32_DLL.INSTANCE.GetLastError();
if((!writeResult) || lastError!=0)
{
result.setLastError(lastError);
result.setMessage("内存写入发生错误,错误代码:"+lastError);
return result;
}
result.setLastError(0);
result.setMessage("写入成功!");
return result;
}
catch (Exception e)
{
result.setLastError(-1);
result.setMessage("写入失败,请检查输入值是否正确或超出范围!\n错误代码:"+e.getMessage());
}
finally
{
Kernel32_DLL.INSTANCE.CloseHandle(hProcess);
}
return result;
}
}
代码六:进程的杀死实现
package com.memory.impl;
import com.memory.entity.ExecuteResult;
import com.memory.interfaces.Kernel32_DLL;
import com.memory.quantity.OpenProcess;
import com.sun.jna.ptr.IntByReference;
/**
* 进程杀死实现类
* 作者:Code菜鸟
* */
public class KillProcess
{
/**
* 具体解释,请查看com.memory.interfaces.Kernel32_DLL接口中的描述
* pid 进程ID,这个值可以通过任务管理器查看或通过CreateToolhelp32Snapshot函数获取
* **/
public ExecuteResult kill(int pid)
{
ExecuteResult executeResult = new ExecuteResult();
int hProcess = Kernel32_DLL.INSTANCE.OpenProcess(OpenProcess.PROCESS_ALL_ACCESS, false, pid);
//INT指针,保存GetExitCodeProcess函数调用成功后,返回的程序退出值
IntByReference lpExitCode = new IntByReference();
try
{
//获取程序的退出代码
if(Kernel32_DLL.INSTANCE.GetExitCodeProcess(hProcess, lpExitCode))
{
//退出程序
Kernel32_DLL.INSTANCE.TerminateProcess(hProcess, lpExitCode.getValue());
}
}
finally
{
//释放INT指针
ReferenceFree.free(lpExitCode);
Kernel32_DLL.INSTANCE.CloseHandle(hProcess);
}
//获取执行结果
int lastError = Kernel32_DLL.INSTANCE.GetLastError();
executeResult.setLastError(lastError);
if(lastError!=0)
executeResult.setMessage("杀死进程时发生错误,错误代码:"+lastError);
return executeResult;
}
}
















