前几天写了个QQ游戏练练看的外挂,不过实现原理很简单,先是从内存中读出练练看的棋盘数据,然后再用算法进行分析,得到两个可以连接的棋子坐标,再用软模拟鼠标在游戏窗口内点击两个棋子。
如果要做网络游戏的外挂,那么就要找游戏里的CALL,然后再在该游戏进程里注入汇编码,再执行注入的汇编码,来实现调用游戏CALL,那么这里就要用的汇编,其实并不是Java内联汇编,因为在Java里写汇编码,JAVA的JVM是不认识的,现在我们就要用的JNA,在系统函数里有这几个函数:
public HANDLE CreateRemoteThread(W32API.HANDLE hProcess,Structure lpThreadAttributes,int dwStackSize,int lpStartAddress,Structure lpParameter,int dwCreationFlags,IntByReference lpThreadId);
这个函数是调用远程线程。
public int VirtualAllocEx(W32API.HANDLE hProcess,IntByReference lpAddress,int dwSize,int flAllocationType,int flProtect);
这个函数是在指定进程内开辟一段内存空间。
public boolean WriteProcessMemory(W32API.HANDLE hProcess,int lpBaseAddress,byte []lpBuffer,int nSize,IntByReference lpNumberOfBytesWritten);
这个函数是在一段内存空间内,写入指定汇编码。
具体每个参数的意思可以在MSDN里面找到。
刚开始我是用StringBuffer把汇编码组成一个字符串,然后再转换成字节数组,写入内存,在去调用,在测试的时候,发现会把游戏直接送回娘家,游戏直接挂掉,让我纳闷了几个小时,到底是哪里出错了啦?在网上搜索发现,有个牛人已经用JAVA做过内联汇编,查看他的源代码才发现,汇编码不能直接转换成byte数组写入进去,必须要转换成机器码写入才能调用,下面把源码附上:
//获得窗口句柄
W32API.HWND hwnd=User32.INSTANCE.FindWindow(null, "GameCall");
//获得窗口进程ID
IntByReference id=new IntByReference();
User32.INSTANCE.GetWindowThreadProcessId(hwnd,id);
//获得进程句柄
W32API.HANDLE handle=MyKernel32.INSTANCE.OpenProcess(MyKernel32.PROCESS_ALL_ACCESS, false, id.getValue());
//开辟内存空间
int l=MyKernel32.INSTANCE.VirtualAllocEx(handle, null, 0x3000, 0x1000, 0x40);
if(l==0){
System.out.println("分配内存失败");
return;
}else{
System.out.println("分配内存成功");
System.out.println("内存地址:"+l);
}
//编写汇编码
ASM asm = new ASM();
//寄存器全部入栈
asm._PUSHAD();
//写入CALL汇编码
asm._CALL(0x401cc0); //0x401cc0这个是CALL的基址.
//寄存器全部出栈
asm._POPAD();
// 结尾标记,操作开始执行
asm._RET();
//将汇编码写入内存
boolean b=MyKernel32.INSTANCE.WriteProcessMemory(handle, l,ASM.getHexToBytes(asm.getASMCode()), 0x3000, null);
if(b){
System.out.println("写入成功");
}else{
System.out.println("写入失败");
return;
}
//连续调用写入的汇编码
for(int i=0;i<50;i++){
int lpThreadId=0;
MyKernel32.INSTANCE.CreateRemoteThread(handle, null, 0, l,null , 0, lpThreadId);
if(lpThreadId==0){
System.out.println("调用成功");
}else{
System.out.println("调用失败");
}
}
好了,今天就写到这,继续去研究呢。