程序多开原理记录

代码
windows系统下,程序防止多开的几种常见方法: 
1 )使用FindWindow API函数。 
通过查找窗口标题(或
/ 和类名)来判断程序是否正在运行。如果找到了,表明程序正在运行,这时可退出程序,达到不重复运行的效果;反之表明程序是第一次运行。 
这种方法不适用于以下情况,程序的标题是动态变化的、系统中运行了相同标题(或
/ 和类名)的程序

2 )Mutex / Event / Semaphore 
通过互斥对象
/ 信号量 / 事件等线程同步对象来确定程序是否已经运行。最常用的函数如:CreateMutexA(注意:QQ堂、QQ游戏大厅就是采用这样方法来限制程序多开的)

3 )内存映射文件(File Mapping) 
通过把程序实例信息放到跨进程的内存映射文件中,也可以控制程序多开。

4 )DLL全局共享区 
DLL全局共享区在映射到各个进程的地址空间时仅被初始化一次,且是在第一次被windows加载时,所以利用该区数据就能对程序进行多开限制。

5 )全局Atom 
将某个特定字符串通过GlobalAddAtom加入全局原子表(Global Atom Table),程序运行时检查该串是否存在来限制程序多开。(该Atom不会自动释放,程序退出前必须调用GlobalDeleteAtom来释放Atom)

6 )检查窗口属性 
将某些数据通过SetProp加入到指定窗口的property list,程序运行时枚举窗口并检查这些数据是否存在来限制多开。

以上只列举了最常见的几种方法,具体应用中可以有n种选择,或综合运用多种方法来限制。

上面说过,QQT采用CreateMutex函数来限制多开,那么我怎么知道是使用这个函数来限制的呢? 
答案就是跟踪程序,查找程序是使用哪种方法来限制的。比如先看看是否使用CreateMutex来限制,如果不是,再看看是不是使用FindWindow,以此类推,直到找到方法为止。当然,有些程序也会综合使用多种方法来限制多开,方法也是一样的,只是麻烦点而已。

下面讲一讲使用CreateMutex函数来限制多开的方法: 
CreateMutex函数声明如下(具体请查阅相关资料,如MSDN) 
HANDLE CreateMutex( 
LPSECURITY_ATTRIBUTES lpMutexAttributes,
//  pointer to security attributes 
BOOL bInitialOwner,  //  flag for initial ownership 
LPCTSTR lpName //  pointer to mutex-object name 
);

以下是使用CreateMutex函数来限制多开的典型delphi代码 
hMutex:
= CreateMutex(nil,TRUE, ' qqtang ' ); // 建立互斥量 
//  调用失败? 已经存在? 
if (hMutex = 0 ) or (GetLastError = ERROR_ALREADY_EXISTS)then 
begin 
// 程序第二(或以上)次运行时,GetLastError会返回ERROR_ALREADY_EXISTS,表明互斥量已存在 
// 可以在这里编写退出代码 
end; 
该段代码首先调用CreateMutex函数创建一名为 qqtang 的互斥对象,如果调用CreateMutex函数失败(hMutex
= nil)或互斥对象早已存在(GetLastError = ERROR_ALREADY_EXISTS),则退出程序。

好了,明白上面的内容后,我们进入修改实战: 
下载OllyDbg V1.
1 ,解压到任何目录即可使用。 
启动OllyDbg,打开QQT目录下的Core.dll文件,按[是]载入DLL文件。 
按Ctrl
+ N打开API调用列表,找到CreateMutexA后按回车,在弹出的窗口里双击第一行来到CPU窗口,反汇编代码如下:

10002FB9 . 
51  push ecx ;  / MutexName  =   " qqtang "  
10002FBA . 6A 
01  push  1  ;  | InitialOwner  =  TRUE 
10002FBC . 6A 
00  push  0  ;  | pSecurity  =  NULL 
10002FBE . FF15 60E40010 call dword ptr [
<& KERNEL32.CreateMutexA > ] ; \CreateMutexA 建立互斥量 
10002FC4 . 8B95 D4FEFFFF mov edx,dword ptr [ebp
- 12C] 
10002FCA . 
8902  mov dword ptr [edx],eax 
10002FCC . 8B85 D4FEFFFF mov eax,dword ptr [ebp
- 12C] 
10002FD2 . 
8338   00  cmp dword ptr [eax], 0  ; 检查CreateMutexA函数是否调用失败 
10002FD5 . 0F84 CD000000 je Core.100030A8 ; 把je改为jmp即可 
10002FDB . FF15 5CE40010 call dword ptr [
<& KERNEL32.GetLastError > ] ; [GetLastError 
10002FE1 . 3D B7000000 cmp eax,0B7 ; 检查对象是否已存在 
10002FE6 . 0F85 BC000000 jnz Core.100030A8 ; (也可以在这里把jnz改为jmp) 
10002FEC . 8B8D D4FEFFFF mov ecx,dword ptr [ebp
- 12C] 
10002FF2 . C701 
00000000  mov dword ptr [ecx], 0  
10002FF8 . 6A 
00  push  0  ;  / Title  =  NULL 
10002FFA . 
68  5CC60010 push Core.1000C65C ;  | Class  =   " QQTangWinClass "  
10002FFF . 6A 
00  push  0  ;  | hAfterWnd  =  NULL 
10003001  . 6A  00  push  0  ;  | hParent  =  NULL 
10003003  . FF15 40E70010 call dword ptr [ <& USER32.FindWindowExA > ] ; \FindWindowExA 查找QQT窗口

选中这行: 
10002FD5 . 0F84 CD000000 je Core.100030A8 
然后按空格,在弹出的窗口中把“je 100030A8”修改为“jmp 100030A8”,按[汇编]。 
右键单击CPU窗口,在弹出菜单中选“复制到可执行文件”
- 》“所有改动”,选[全部复制]。右键单击弹出的窗口,选“保存文件”保存即可。

是否觉得上面的修改比较麻烦呢?呵呵,授人于鱼不如授人于渔,上面是告诉你为什么要这样修改,修改的原理是什么,你明白修改原理后,有新版本时你就可以自己修改了。

 

你可能感兴趣的:(原理)