能力值:
( LV6,RANK:90 )
2 楼
标 题:程序运行实例数量的控制——大全篇[原创]
发信人:bellkwong
时 间:2004-3-28 周日, 上午12:44
详细信息: 经常会碰到有人问如何保证程序只运行一个实例,原来我也零碎的给过两三个方法,今天干脆来个大总结,希望对大家在做程序设计的时候有所帮助。
一个程序只运行一个实例(或限制实例数量)通常可以采用如下方法:
1)FindWindow 之<窗口标题>
通过查找窗口标题来确定上一实例是否正在运行,不适合窗口标题动态变化的程序。
2)FindWindow 之<任务栏按纽标题>
通过查找任务栏按纽标题来确定上一实例是否正在运行,不适合按纽标题动态变化的程序(如Winamp)。通常情况下,该方法还是优先考虑,因为按纽标题是一般是固定的。
3)Window Property
将某个数据(可以是字符串或句柄)通过SetProp加入到指定窗口的property list,程序运行时枚举窗口并检查该数据是否存在来确定上一实例是否正在运行。
4)全局Atom
将某个特定字符串通过GlobalAddAtom加入全局原子表(Global Atom Table),程序运行时检查该串是否存在来确定上一实例是否正在运行。该方法有个局限,就是程序终止前必须显式调用GlobalDeleteAtom来释放atom,否则该atom不会自动释放,如果程序运行时意外终结了,那么下一个实例就无法正常执行。早期版本的realplayer就存在这个现象,不知道是不是采用了该方法。
5)Mutex/Event/Semaphore
通过互斥对象/信号量/事件等线程同步对象来确定实例是否存在,在NT下要注意权限问题(SID)。
6)DLL全局共享区域
VC下的DLL工程可以通过下面代码来建立一个进程间共享数据段:
#pragma data_seg(".share")
//shared for all processes that attach to the dll
DWORD dllgs_dwRunCount = 1; //一定要在这里对变量进行初始化,否则工夫白做!
#pragma data_seg()
#pragma comment(linker,"/section:.share,rws")
导出3个函数,分别为:
DWORD IncRunCount(void); //运行计数器加1,返回计数器结果
DWORD DecRunCount(void); //运行计数器减1,返回计数器结果
DWORD GetRunCount(void); //取当前运行计数器
由于DLL全局共享段在映射到各个进程地址空间时仅会被初始化一次,并且是在首次被windows加载时,所以利用该共享段数据就能对程序实例进行可靠计数。
7)内存映射文件(File Mapping)
通过把程序实例信息(如窗口句柄、计数器等等)放置到跨进程的内存映射文件,同样可以控制程序实例运行的数量,道理与DLL全局共享区域类似。
8)其它
曾经见过有人通过注册表、磁盘文件等途径来处理实例控制问题,但由于这些参考对象均为非易失性资源,在碰到程序非正常结束且没有清除实例标识时相当麻烦,真正使用起来具有很大的局限性。
总结:前面三种方法适用于拥有窗体的程序,而后面几种则没有这个限制,但相对而言后者实现起来较复杂。不管采用哪种方法,参考对象均必须具有可共享、跨进程、易失性、重启自复位等必要性质。
能力值:
( LV2,RANK:10 )
3 楼
感谢你的回答,
问题是上面方法不试用呀,
因为标题会变,执行程序名字也会变
能力值:
( LV2,RANK:10 )
4 楼
2楼列了这么多方法不可能都不适用吧?找一找特征,你可以想一下,你自己是怎么用肉眼识别出这个程序的。
能力值:
(RANK:260 )
5 楼
rxzcums提到的方法是对于自己编写的程序,如何让它单实例运行。
关于这个,王艳平《Windows程序设计》中讲到过几种方法。我最乐于使用的方法是使用命名互斥量来实现。
如果你是在判断第三方的程序是否在运行,那就稍微麻烦点。通常情况下,比较稳妥的办法是对程序主窗口的窗口类名进行判断。
但我说了,通常情况是可以的,因为一般人写程序,不管是SDK还是其它类库,通常使用常量作为窗口类名。
一个最常见的”非一般情况“,比如pjf的IS,它的窗口类名是随机生成的,就是为了防止有人用此方法来查找。
能力值:
( LV2,RANK:10 )
6 楼
程序不是自己写的,是外部程序~
能力值:
(RANK:260 )
7 楼
请仔细阅读我的回复,我已经说过了,通常的程序可以通过窗口类的名称来判断。
就提示到这时。具体实现方法请自己查资料。
PS.如果你是想对安全软件下手,请学习内核技术。基本所有的安全软件都无法在ring3下结束,当然少数存在漏洞的除外。
能力值:
(RANK:280 )
8 楼
EnumProcesses或Process32First/Next枚举进程,接着OpenProcess打开,然后ReadProcessMemory读进程数据判断是否是要找的进程
如果全部进程都检查过还是没有发现要找的进程就说明该程序不在运行
能力值:
( LV2,RANK:10 )
9 楼
类名查找我试过,但是标题会变,有时候都没标题,难以下手啊~
我现在是用循环读取进程模块检测运行的DLL 但是如果是绿色程序,没附带DLL我就没办法了
能力值:
( LV2,RANK:10 )
10 楼
原先我也是这样写的,但是不知道应该解决那段内存数据,因为会变化,所以我就放弃了~
能力值:
(RANK:260 )
11 楼
是类名,不是标题。
我上面也说过了,有的程序确实会用动态地类名,目的就是防止检测,比如pfj的IS。不过IS即使检测到了,你也无法在ring3下结束它。
如果真碰上厉害的防止检测的程序,只能用一些人工智能的方法。不过只是想法而已,我没试过。
能力值:
( LV6,RANK:90 )
12 楼
支持 书呆彭 的观点
窗口类名称(Class) 基本是不变的,这点也可以从很多 Loader 制作工具中得到证实:
1、Standard
2、Window Caption
3、Window Class
能力值:
( LV2,RANK:10 )
13 楼
窗口类的名称基本是不变.这个我知道 ,但是90%的在电脑的软件类名都是一样的,这样没办法判断,因为标题会变,有时候都没标题~
能力值:
( LV2,RANK:10 )
14 楼
我知道,但是你光查类名会查不一大堆的,想准确找出来只能 类名 + 标题
能力值:
( LV6,RANK:90 )
15 楼
能不能依次读入各个进程的某个内存地址然后检测该地址的Dword值来确定是否是要找的进程?1个还不保险的话就2个、3个,都对得上的话应该就能确认是你要找的进程了。
瞎想的,错了别拍砖哦
能力值:
(RANK:260 )
16 楼
其实乌龟大师给出的办法是非常优雅的。
既然它标题也变,程序名也变,我们总要找它不变的地方。
程序本身不会变。
所以,我们就搜索程序代码中的特征码。
枚举所有进程,找到程序加载基址(多数情况下可以硬编码为0x400000),然后ReadProcessMemory,寻找它的特征,或者求hash值,来进行比较。
比较可行的方法,应该是到数据区,特定的程序所使用的特定常量,位于特定的地址。
另一个思路是从进程中取得它可执行文件的全路径,然后直接对文件进行判断,比如hash比较和文件大小比较等。
能力值:
( LV2,RANK:10 )
17 楼
这样行是行,就是不知道应该读取那段数据,因为内存数据会变动,我对内存也不了解,也不知道哪个区域的数据不会变~
能力值:
( LV6,RANK:90 )
18 楼
随便找代码段的值
比如
00401000
00401111
。。。
这个地址里的dword应该是不会变的,而且不同进程相同地址的机器码不会相同的,我说了,多找几处,全部符合要求就认为是要找的进程。
能力值:
( LV2,RANK:10 )
19 楼
恩,要是可以知道内存中那端数据是不会边的就好了~
能力值:
( LV2,RANK:10 )
20 楼
我看内存地址可能难以判断是否是固定地址.
到可以判断静态的存盘问题,
那请问各位老师,
静态的存盘文件他主要都有那些特征的?那些是唯一性的呢? 比如说判断 头文 或是其他唯一不变,其他程序也不一样的呢?
能力值:
( LV6,RANK:90 )
21 楼
我不认为程序运行起来之后他内存里的代码段的值还在不停的变。
就算加壳,加猛壳,他运行起来之后代码就解压到内存不会再变了吧。
照你这么说的话 Loader 也太没前途了
能力值:
( LV2,RANK:10 )
22 楼
我不是说他一直在变,只是说,查内存数据我不怎么了解,不能分清楚哪个地址是固定的还是动态的
我选择的判断的这个区域内存数据是会变的还是不会变的
能力值:
( LV6,RANK:90 )
23 楼
代码段、资源段、、、好多都可以吧
你就从OEP处开始,隔xx个字节取个dword
能力值:
( LV2,RANK:10 )
24 楼
你可以告诉我你QQ号吗?
我还有很多地方不懂,希望可以向你请教
能力值:
( LV6,RANK:90 )
25 楼
找大牛吧,书呆彭 就是大牛!那个 乌龟 大师就更恐怖了。
你让我说个想法思路可以,但是具体编程方面的操作我不会啊