免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
内容参考于:易道云信息技术研究院
上一个内容:97.游戏的启动与多开-共享内存多开检测
以 97.游戏的启动与多开-共享内存多开检测 它的代码为基础进行修改
效果图:
检测的算法:信号量、互斥量、进程枚举、窗口检测、共享内存等方式
检测一个游戏使用的什么多开检测算法可以把 信号量、互斥量、进程枚举、窗口检测、共享内存等方式 用到的函数全部HOOK掉然后把它注入到游戏进程中,这样可以得到游戏是用了什么方式实现的检测多开。
然后一个程序正常启动之后会进入Windows的消息循环,一个有检测多开的程序会结束消息循环,然后从程序启动到结束这中间执行的 信号量、互斥量、进程枚举、窗口检测、共享内存等方式把它们hook之后就可以知道具体使用的是信号量还是互斥量等检测检测多开的了。
进入消息循环会很大几率调用下图中的函数,逆向分析的话可以从下图的代码入手,检测多开一定会在它之前,这种方式叫倒着往上推
使用启动的方式分析剑侠情缘的多开检测:
首先打开一个剑侠情缘
然后再使用 Ollydbg.exe打开一个剑侠情缘,这个剑侠情缘是打不开的,因为上面打开过了,这里是为了跟踪它退出的代码,然后使用打开的方式运行剑侠情缘
打开之后会自动在入口点断点主
然后把所有的CALL都打上断点看看游戏是从哪个函数里结束的
然后它会从下图红框位置的函数中结束
然后进入跟踪,下图 0042C543 位置的代码是DS:[&KERNEL32.ExitProcess]这样的写法,这种写法是指针调用函数的表现形式,也就是 ExitProcess 在编译的时候是不知道它的内存地址是什么的,它的内存地址只有Windows操作系统知道,当程序运行时Windows操作系统会给它填充上
下图位置有一个有意思的写法,push edi push 1 pop edi,如果把pop edi写到别的地方可以进行迷惑敌人达到防御的效果
然后它会执行到下图红框位置调用 ExitProcess函数退出程序,然后把所有的剑侠情缘进程关闭,使用Ollydbg.exe跟一次不会被多开退出的情况
然后发现它会从下图红框位置无法退出,然后程序也正常运行了,也就是进入了Windows的消息循环
然后点关闭的时候又会来到下图位置的函数中
然后跟随一下进入消息循环的函数,可以看到它来到了之前分析命令行的函数中
然后一路f8就会看到下图红框位置的代码,这里很明显剑侠情缘使用的是窗口检测的方式,然后如果把409334位置改成EB也就是jmp指令那么就可以破解它的多开了
实现代码:有两种方式通过cheater项目的回调函数和通过dlls项目的dll
cheater项目的回调函数的方式实现
void _llback(LPPROCESS_INFORMATION lpi) {
char data[]{ 0xEB };
WriteProcessMemory(lpi->hProcess, (LPVOID)0x409334, data, sizeof(data), 0);
}
void ChtdExeDlg::OnBnClickedOk()
{
/**
InjectByWndHook函数是窗口注入:
Sword2 Window是从Spy++里得到,这是窗口的名字(标题)
Sword2 Class是从Spy++里得到,这时窗口的类
dlls.dll是dlls项目中编译出来的dll文件
*/
//htd::INJECT::InjectByWndHook(L"Sword2 Window", L"Sword2 Class",L"dlls.dll");
/**
* InjectByEntry函数是入口点注入
* 第一个参数是游戏可执行文件的目录
* 第二个是游戏目录
* 第三个是启动游戏的命令行参数
* 第四个是外挂模块的目录
*/
htd::INJECT::InjectByEntry(txtPath,
txtAppPath,
L"",
txtDllPath, _llback);
// TODO: 在此添加控件通知处理程序代码
//CDialogEx::OnOK();
/*htd::INJECT::InjectByEntry(L"F:\\Games\\JX2\\Sword2WindowsA7.exe",
L"F:\\Games\\JX2\\",
L"",
L"F:\\Program Design\\课程代码\\htd\\Release\\htdMfcDll.dll");
*/
//htd::INJECT::InjectByWndHook(L"Sword2 Window",L"Sword2 Class",L"F:\\Program Design\\课程代码\\htd\\Release\\htdMfcDll.dll");
}
dlls项目的方式实现
DWORD dOld;
VirtualProtect((LPVOID)0x409334, 1, PAGE_EXECUTE_READWRITE, &dOld);
char* data = (char*)0x409334;
data[0] = 0xEB;