木马编程之单实例运行

作者在 2008-11-29 15:27:55 发布以下内容
实现思路
来源:黑白网络  作者:  日期:2008-05-12  

这类程序的典型代表就是木马的服务端了,我们想要达到目得,会有这样的想法:程序运行时先检查有没有另一
个实例在运行,没有的话就运行自己,有的话就退出自己.

编程实现

通常有两种方法可以实现我们分别来介绍

1.使用互斥对像


使用API函数CreateMutex来创建命名互斥对象来实现程序互斥是一个比较通用的方法,我们可以在主函数加入如下代码:

HANDLE hObject = CreateMutex(NULL,FALSE,"LengFeng");

if(GetLastError() == ERROR_ALREADY_EXISTS)//程序已经运行
{
        CloseHandle(hObject);
        ExitProcess(0);
}
以上的CreateMutext函数创建一个称为"LengFeng"的命名的互斥对象,当程序的第二个实例运例时
调用CreateMutex 返回ERROR_ALREADY_EXISTS,表示名为"LengFeng"的互斥对像以存在,也就是说明程序以运行

其完整代码如下:

#include <windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
        HANDLE hObject = CreateMutex(NULL,FALSE,"LengFeng");
        if(GetLastError() == ERROR_ALREADY_EXISTS)
        {
                CloseHandle(hObject);
                MessageBox(NULL,"程序已经运行!",NULL,NULL);
                ExitProcess(0);
        }
        while(1)
        {}
        return 0;
}
使用一个循环是禁止程序退出,以便更好的观察效果^_^

2.用编译器创建新节

能不能为程序中加入一个全局变量?让这个全局变量可被程序的多个实例所共享,每当程序实例运行时就对该
全局变量进行修改,通过对该全局变量的访问,就可以知道有多少个实例在运行了.
当然为了系统的安全和稳定性,默认情况下是不允许这样做的,为了阻止这种事情的发生系统使用copy-on-write(写入时拷贝)机制
不过我们就使用创建新节的方法来绕过系统的copy-on-write机制其方法如下

#pragma data_seg("Shared")    
int volatile g_lAppInstance =0;
#pragma data_seg()

我们来看一下上面的内容:
第一句 #pragma data_seg("Shared") 创建一个称为Shared 的新节。
第二句 int volatile g_lAppInstance =0 将 g_lAppInstance 放入Shared节中。注意此时只有将g_lAppInstance初始化,编译器才会将其放入Shared节中,否则,将放入Shared以外的节
第三句指示编译器Shared 节结束。
仅仅告诉编译器将某些变量放入它们自己的节中,是不足以实现对这些变量的共享的。还必须告诉链接程序
某个节中的变量是需要加以共享的。若要进行这项操作,可以使用链接程序的命令行上的/SECTION开关:
在冒号的后面,是想要改变其属性的节的名字。在我们的例子中,想要改变Shared节的属性因此应该创建下面的链接程序开关:
#pragma comment(linker,"/section:Shared,RWS")
这一句,我们使编译链接器知道我们的Shared节具有读,写,共享的属性。这是我们实现互斥运行的关键
这样我们就可以在应用程序之间的多个实例之间共享g_lAppInstance 变量了。

下面我们看一下在SDK程序中它的完整实现代码如下:

#include <windows.h>
#pragma  data_seg("Shared")                //创建新节
int volatile g_lAppInstance =0;                //必须有初示化        
#pragma  data_seg()                        //结束

#pragma comment(linker,"/section:Shared,RWS")

 

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
        if(++g_lAppInstance>1)
        {
                MessageBox(NULL,"程序已经运行!",NULL,NULL);
                ExitProcess(0);
        }
        while(1)
        {
        }

}

回头再看这两种方法,在实现上使用互斥对像比较简单,但功能却不如创建新节的方法强大,不过在无窗口程序中
使用互斥对像还是比较实用的.

 

基于窗口程序的实现


在有些地方是用EnumWindows来枚举窗口并结合FindWindow来查找窗口,然后发送WM_CLOSE消息,虽然这种方法也可以实现
但实现效果不是太好....而使用上面介绍的互斥对像也是可以的,但它的不足之处是不能将已经启动的实例激活。

这里我们仍然用“创建新节”方法来实现一下

目标是一个基于对话框的程序,其效果如图所示

其实现代码如下

#pragma data_seg("Shared")    
HWND hwnd=NULL;
#pragma data_seg()
#pragma comment(linker,"/section:Shared,RWS")


在对话框OnInitDialog()中加入以下代码:

if(hwnd==NULL)
{
        hwnd=m_hWnd;//没有其它实例则保存实例句柄
}else
{
        ::SetForegroundWindow(hwnd);//若有其它实例则激活
        ExitProcess(0);//退出本身
}

当然上面的这些代码完全可以无需修改的加入到自己的程序中,用这种方法实现在感觉上是不是比PostMessage(hwnd,WM_CLOSE,0,0)要文明多了?
windows网络编程 | 阅读 2502 次
文章评论,共1条
lanqiaojun(作者)
2008-11-29 16:27
1
注意`ExitProcess(0)之后无法释放NEW的内存`
游客请输入验证码