最近闲着蛋疼,无聊之中用CE耍了一哈游戏,发现挺好用的,于是就想,我自己能不能写个内存修改器呢?于是乎,这个软件就诞生了!

当然我只会Java,C++嘛,了解了解,嘿嘿,所以这个工具也是用Java写的


这个工具在写内存搜索时,简直累死了,最开始暴力搜索,效率慢的要死,经过三天的不懈努力,终于搞定这搜索功能!这也是整个工具最难的部分,至少我是这么认为的

微软的MSDN都被我翻了又翻 - - !



下载解压后的目录结构:

java修改器方法 用java写修改器_java

第一个是源代码:用Eclipse导入即可,记得修改Build Path

第二个是打包好的Jar,如果电脑安装了Java,就可以直接运行

第三个是打包好的exe安装程序,可以在没有安装Java的电脑上运行


不多说了,先上一张效果图: (这里用植物大战僵尸做实验,4399的小游戏)

原来阳光是150,被我改成1024了

java修改器方法 用java写修改器_java修改器方法_02


先说说这个软件的使用步骤吧,与CE用法差不多,先打开游戏进程,输入搜索的游戏值(比如这里改阳光,所以输入150),然后开始搜索,搜索完成后,种一柱植物,或者捡一个阳光,总之让阳光值发生变化,然后再点击搜索变化,地址就出来啦,最后,鼠标点击,输入修改值,点击写入内存就可以咯

如果搜索变化没有,就多搜两次哦


本工具开发环境如下:

开发工具:Eclipse

开发语言:Java

开发系统:windows7

JDK版本:1.6


项目所需的第三方包:

官网下载JNA包:https:///java-native-access/jna   这个包用于调用windows系统函数库

官网下载皮肤包:http://www.jtattoo.net/   这个包是Swing的皮肤包,用于做界面用的


代码实现大致思路如下:

获取Debug权限->创建系统快照->获取进程ID->获取进程在内存中的首地址与结束地址->打开进程->遍历内存->查找数据->修改数据


项目包目录如下:

java修改器方法 用java写修改器_java_03


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;
	}
}