﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-elva-随笔分类-外挂技术</title><link>http://www.cppblog.com/elva/category/6188.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 18:24:18 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 18:24:18 GMT</pubDate><ttl>60</ttl><item><title>程序多开原理记录</title><link>http://www.cppblog.com/elva/archive/2008/02/19/42923.html</link><dc:creator>叶子</dc:creator><author>叶子</author><pubDate>Tue, 19 Feb 2008 00:57:00 GMT</pubDate><guid>http://www.cppblog.com/elva/archive/2008/02/19/42923.html</guid><wfw:comment>http://www.cppblog.com/elva/comments/42923.html</wfw:comment><comments>http://www.cppblog.com/elva/archive/2008/02/19/42923.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/elva/comments/commentRss/42923.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/elva/services/trackbacks/42923.html</trackback:ping><description><![CDATA[<p>windows系统下，程序防止多开的几种常见方法： <br>1)使用FindWindow API函数。 <br>通过查找窗口标题(或/和类名)来判断程序是否正在运行。如果找到了，表明程序正在运行，这时可退出程序，达到不重复运行的效果；反之表明程序是第一次运行。 <br>这种方法不适用于以下情况，程序的标题是动态变化的、系统中运行了相同标题（或/和类名）的程序</p>
<p>2)Mutex/Event/Semaphore <br>通过互斥对象/信号量/事件等线程同步对象来确定程序是否已经运行。最常用的函数如：CreateMutexA（注意：QQ堂、QQ游戏大厅就是采用这样方法来限制程序多开的）</p>
<p>3)内存映射文件(File Mapping) <br>通过把程序实例信息放到跨进程的内存映射文件中，也可以控制程序多开。</p>
<p>4)DLL全局共享区 <br>DLL全局共享区在映射到各个进程的地址空间时仅被初始化一次，且是在第一次被windows加载时，所以利用该区数据就能对程序进行多开限制。</p>
<p>5)全局Atom <br>将某个特定字符串通过GlobalAddAtom加入全局原子表(Global Atom Table)，程序运行时检查该串是否存在来限制程序多开。(该Atom不会自动释放，程序退出前必须调用GlobalDeleteAtom来释放Atom)</p>
<p>6)检查窗口属性 <br>将某些数据通过SetProp加入到指定窗口的property list，程序运行时枚举窗口并检查这些数据是否存在来限制多开。</p>
<p>以上只列举了最常见的几种方法，具体应用中可以有n种选择，或综合运用多种方法来限制。</p>
<p>上面说过，QQT采用CreateMutex函数来限制多开，那么我怎么知道是使用这个函数来限制的呢？ <br>答案就是跟踪程序，查找程序是使用哪种方法来限制的。比如先看看是否使用CreateMutex来限制，如果不是，再看看是不是使用FindWindow，以此类推，直到找到方法为止。当然，有些程序也会综合使用多种方法来限制多开，方法也是一样的，只是麻烦点而已。</p>
<p>下面讲一讲使用CreateMutex函数来限制多开的方法： <br>CreateMutex函数声明如下（具体请查阅相关资料，如MSDN） <br>HANDLE CreateMutex( <br>LPSECURITY_ATTRIBUTES lpMutexAttributes,// pointer to security attributes <br>BOOL bInitialOwner, // flag for initial ownership <br>LPCTSTR lpName// pointer to mutex-object name <br>);</p>
<p>以下是使用CreateMutex函数来限制多开的典型delphi代码 <br>hMutex:=CreateMutex(nil,TRUE,'qqtang');//建立互斥量 <br>// 调用失败? 已经存在? <br>if(hMutex=0) or (GetLastError=ERROR_ALREADY_EXISTS)then <br>begin <br>//程序第二(或以上)次运行时，GetLastError会返回ERROR_ALREADY_EXISTS，表明互斥量已存在 <br>//可以在这里编写退出代码 <br>end; <br>该段代码首先调用CreateMutex函数创建一名为 qqtang 的互斥对象，如果调用CreateMutex函数失败（hMutex=nil）或互斥对象早已存在（GetLastError=ERROR_ALREADY_EXISTS），则退出程序。</p>
<p>好了，明白上面的内容后，我们进入修改实战： <br>下载OllyDbg V1.1，解压到任何目录即可使用。 <br>启动OllyDbg，打开QQT目录下的Core.dll文件，按[是]载入DLL文件。 <br>按Ctrl+N打开API调用列表，找到CreateMutexA后按回车，在弹出的窗口里双击第一行来到CPU窗口，反汇编代码如下：</p>
<p>10002FB9 . 51 push ecx ; /MutexName = "qqtang" <br>10002FBA . 6A 01 push 1 ; |InitialOwner = TRUE <br>10002FBC . 6A 00 push 0 ; |pSecurity = NULL <br>10002FBE . FF15 60E40010 call dword ptr [&lt;&amp;KERNEL32.CreateMutexA&gt;] ; \CreateMutexA 建立互斥量 <br>10002FC4 . 8B95 D4FEFFFF mov edx,dword ptr [ebp-12C] <br>10002FCA . 8902 mov dword ptr [edx],eax <br>10002FCC . 8B85 D4FEFFFF mov eax,dword ptr [ebp-12C] <br>10002FD2 . 8338 00 cmp dword ptr [eax],0 ; 检查CreateMutexA函数是否调用失败 <br>10002FD5 . 0F84 CD000000 je Core.100030A8 ; 把je改为jmp即可 <br>10002FDB . FF15 5CE40010 call dword ptr [&lt;&amp;KERNEL32.GetLastError&gt;] ; [GetLastError <br>10002FE1 . 3D B7000000 cmp eax,0B7 ; 检查对象是否已存在 <br>10002FE6 . 0F85 BC000000 jnz Core.100030A8 ; (也可以在这里把jnz改为jmp) <br>10002FEC . 8B8D D4FEFFFF mov ecx,dword ptr [ebp-12C] <br>10002FF2 . C701 00000000 mov dword ptr [ecx],0 <br>10002FF8 . 6A 00 push 0 ; /Title = NULL <br>10002FFA . 68 5CC60010 push Core.1000C65C ; |Class = "QQTangWinClass" <br>10002FFF . 6A 00 push 0 ; |hAfterWnd = NULL <br>10003001 . 6A 00 push 0 ; |hParent = NULL <br>10003003 . FF15 40E70010 call dword ptr [&lt;&amp;USER32.FindWindowExA&gt;] ; \FindWindowExA 查找QQT窗口</p>
<p>选中这行： <br>10002FD5 . 0F84 CD000000 je Core.100030A8 <br>然后按空格，在弹出的窗口中把&#8220;je 100030A8&#8221;修改为&#8220;jmp 100030A8&#8221;，按[汇编]。 <br>右键单击CPU窗口，在弹出菜单中选&#8220;复制到可执行文件&#8221;-》&#8220;所有改动&#8221;，选[全部复制]。右键单击弹出的窗口，选&#8220;保存文件&#8221;保存即可。</p>
<p>是否觉得上面的修改比较麻烦呢？呵呵，授人于鱼不如授人于渔，上面是告诉你为什么要这样修改，修改的原理是什么，你明白修改原理后，有新版本时你就可以自己修改了。</p>
<img src ="http://www.cppblog.com/elva/aggbug/42923.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/elva/" target="_blank">叶子</a> 2008-02-19 08:57 <a href="http://www.cppblog.com/elva/archive/2008/02/19/42923.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>