一旦把保护的模块文件删掉,之前做的就就白费了。
如何解决?
(1)想办法设计成不能被删除 保护自己的模块(不太可取)
(2)可以删除,但删除后游戏客户端崩溃 让两者高度绑定 使其无法被修复
1.将代码从代码段删除,在此之前要进行几个操作
- 将这段代码从代码段复制下来(有能力可以设计放到虚拟机里执行),可以转移到指定的地方去执行
直接复制下来后,填充二进制CC
所以当代码执行到这个位置的时候会崩溃
所以就要让它跳转到我们的地方去执行,这个时候就要用到寄存器了
找一块好地方来保存,做跳转用之前先保存:
以后每破坏一个函数,都要在前面写下这几个内容
根据push 的数字来决定跳转到的代码是哪一段
首先这个程序启动要在401058写入一个数据,是我们得一个函数,在这个函数里首先得由1个参数来调用我们的函数,就可以返回一个地址,可以根据地址跳转到对应地址。恢复eax寄存器,然后
跳转后代码就接上了,通过这段机制就跳转到自己的流程,在我们的流程将复制下来的那一段数据copy上去,但要注意E8带来的问题,也得做一个系统化的方法,还要记录开始的地址
还有重定位的问题
call xxx
jmp eax
pop eax
保存补丁后,再用之前做好的登录器打开就直接崩溃了
这就有点像在做一个壳:先把入口点破坏,再把代码恢复了
由于字节码比较小就8字节 就不new空间来分配了,
unsigned _stdcall GetFunctionAddress(int index)
{
CString txt;
txt.Format(L"%d", index);
AfxMessageBox(txt);
return 0;
}
GameProtect::GameProtect()//在初始化的时候首先要生成代码段
{
//地址不变 0x401058
auto hMod = GetModuleHandle(0);
unsigned addMod = (unsigned)hMod;
unsigned addReset = addMod + 0x1058;
//修改属性可读可写
DWORD dOld;
::VirtualProtect((LPVOID)addReset, 4, PAGE_EXECUTE_READWRITE, &dOld);
unsigned* read = (unsigned*)addReset;
read[0] = (unsigned)this->_EntryCode;
read = (unsigned*)(this->_EntryCode + 1);
read[0] =(unsigned) GetFunctionAddress - 5 - (unsigned)this->_EntryCode;
}
运行,结果顺利弹出了窗口,表明代码已经顺利进入到函数内了,并且参数也传递对了。
所以在这个stdcall函数里只要根据这个参数来把数据返回回去 很容易得到这是一个字符串,所以要做一个工具将字符串转换为代码。再把E8的地方做出修正
我们这样就会出现问题: 我们把代码迁移了,之前hook的点已经不存在了。
- 手写汇编跳转到自己的函数里重新在我们的函数里再做HOOK
我们就直接用之前写好的hook引擎来调用做hook就好了
我们再来实现运行中多开检测。
因为使用内核对象的方式在内核调试器可以查看的到。我们换一种方式 使用共享内存的方式。
#pragma data_seg("_hdata")
int client = 0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:_hdata,RWS");//设置共享属性
开始的时候++ 销毁的时候-- 只要在游戏启动的时候访问它的数量就好。最好对这个数据进行加密



