利用反汇编技术实现软件多开
客户的软件,用 C++/MFC 开发的,该软件只允许同时运行一个实例,运行第二个时提示不允许,如图1所示。

图1
因为没有源代码,所以只能用反汇编来修改。本文先讲解单开的实现原理,再讲解如何反汇编修改。
1.单开的实现原理
用 C++/MFC 开发的程序默认是允许多开的,单开一定是加了相关代码,比较常见的是使用事件对象。事件对象是一个可以跨进程访问的内核句柄,通常用于进程通信和线程通信。实现单开的代码如下所示:
BOOL CTestMFCApp::InitInstance() {
const char* eventName = "MyEvent";
HANDLE eventHandle = OpenEventA(EVENT_ALL_ACCESS, TRUE, eventName);
if (eventHandle == NULL) {
eventHandle = CreateEventA(NULL, FALSE, FALSE, eventName);
} else {
CloseHandle(eventHandle);
MessageBoxA(NULL, _T("本程序只允许运行一个进程"), _T(""), MB_ICONINFORMATION);
return FALSE;
}
CTestMFCDlg dlg;
m_pMainWnd = &dlg;
dlg.DoModal();
if (eventHandle != NULL) {
CloseHandle(eventHandle);
}
return FALSE;
}
AI写代码
cpp
运行
OpenEventA的第一个参数是访问权限,第二个参数是跨进程访问开关,第三个参数是事件对象的名字。第一个进程在调用 OpenEventA 时会返回 NULL,接着它会调用 CreateEventA 来创建事件对象;第二个进程在调用 OpenEventA 后会得到第一个进程创建的事件对象,这就表明第一个进程还在运行,所以弹出提示后结束进程。
2.利用反汇编实现多开
所谓”反汇编实现多开“,就是用反汇编技术找到调用 OpenEventA 和 CreateEventA 以及判断 OpenEventA 的返回值是否为 NULL 的指令,将这部分指定全改为 NOP(空指令)即可。
首先用反汇编软件 C32ASM 打开要实现多开的软件的主 exe 文件,用搜索功能搜”CreateEventA",搜到的结果如下所示:
::0040110E:: 68 6C344000 PUSH 40346C ->: MyEvent
::00401113:: 6A 01 PUSH 1
::00401115:: 68 03001F00 PUSH 1F0003
::0040111A:: FF15 38304000 CALL NEAR DWORD PTR [403038] >>>: KERNEL32.DLL:OpenEventA
::00401120:: 85C0 TEST EAX, EAX
::00401122:: 0F85 D4000000 JNZ 004011FC :JMPDOWN
::00401128:: 68 6C344000 PUSH 40346C ->: MyEvent
::0040112D:: 50 PUSH EAX
::0040112E:: 50 PUSH EAX
::0040112F:: 50 PUSH EAX
::00401130:: FF15 34304000 CALL NEAR DWORD PTR [403034] >>>: KERNEL32.DLL:CreateEventA
可以看到这里就是上面的 C++ 代码从 OpenEventA 到 CreateEventA 的部分对应的汇编指令(因为没有 C++ 的源代码,我是用汇编指令反推出 C++ 代码的),接下来就简单了,把这些指令全变成空指令即可。可以利用 C32ASM 直接修改,方法是在要修改那行指令上右键,菜单选择“对应 HEX 编辑”,如图2所示。

图2
图3就是对应的 HEX,即二进制。

图3
要从 PUSH 40346C 一直改到 CALL NEAR DWORD PTR [403034],即从 68 6C344000 到 FF15 34304000,如图4所示。

图4
修改后保存并运行,就可以多开了。
补充一点:一定要将 OpenEventA 和 CreateEventA 前面的 Push 指令全改成空指令,因为 Push 是向栈压入参数,在函数(如 OpenEventA)调用结束后栈会被函数清空。但是现在把这两个函数的调用改成空指令了,那 Push 的参数不会被清空,会影响到后面的函数调用。
全文完。

浙公网安备 33010602011771号