您的位置:首页 > 理论基础 > 数据结构算法

Windows服务程序的原理及实现

2010-01-28 15:48 471 查看
今天给大家讲下怎样做一个服务程序...本来是想详细讲的,不过写着写着累得要命..很多
 
地方就没详细...不过代码我加了点注...如果还有一些不明白的自己查下MSDN......便宜
 
环境,,VC++6.0...代码有俩段,一段是服务程序的..另一段是安装服务程序的...这个程序
 
的功能是开机发出滴滴声....安装成功后自己点启动...下次开机就自动起动了....
load.exe的实现是比较简单,本来想弄个汇编版本...不过真的累...就算了..这里一个服
 
务的基本框架就完成了...剩下的只是添加你自己的功能代码...
怎样安装::我在C盘建一个名为sysnap的文件夹..里面放着俩个EXE..一个是sv.exe 另一
 
个是svload.exe 然后cmd到这个文件夹,输入svload.exe则服务安装成功...开机后就有一
 
个服务进程sv.exe...这样我们就有了一个名为sysnap的服务..在注册表里可以找到相关
 
信息....当然你可以把服务名改为你自己的....svload.exe的代码很简单..我没有注
 
释,,,,如果需要我回帖再注..我现在累
 
一个服务的基本框架
 
1感性认识什么是服务
2用INF文件安装服务
3关于服务的一些基本理论知识
4一个服务的创建流程和基本组成
5详说各个基本函数
6所用到的一些数据结构和API说明
7开始我们的第一个服务,完整代码
8运行我们的服务,完整代码
 
1感性认识什么是服务
在运行框输入SERVICES.MSC..看到没,,这些都是windows的服务..里面有一些是windows自己的,有一些是第三方服务,,比如我们的杀毒软件大部分都有一个服务
那服务有什么用呢,..让我们先看一下服务的定义吧
服务:是一种应用程序类型,它在后台运行。
可见如果我们把程序做成服务后也可以照样运行起来...如果是设置成手动的,那开机后我们的服务程序就自动运行起来...所以很多木马都搞成服务启动,,这比在注册表里什么RUN
要隐蔽多了
系统有俩种服务.一种叫win32服务,他运行在用户态,对应的映像文件是.EXE或.DLL..我们这里讲的就是win32服务
另外一种叫系统服务,它运行在内核态,对应的映像文件是.SYS也就是驱动程序..其实这俩个概念现在也没必要细分了吧..区别除了运行态不同外,另外还有一个区别,就是在注册表中除了在HKEY_LOCAL_MACHINE/SYSTE/CurrentControlSet/Services 下都有一个服务名外,系统服务还多了一个设备硬健HKEY_LOCAL_MACHINESystem/CurrentControlSet/Enum子键,因为是驱动程序嘛,在删除一些内核木马时.这个建默认是无法删除的,因为需要SYSTEM权限..不过右键->权限->添加->高级就可以搞定
 
2用INF文件安装服务
现在我们就用.INF文件来把我们的EXE程序变成服务..随开机的运行而运行,这个EXE文件我是用我前几天在吧里发的哪个小程序,依然是输出电脑的用户名...在C盘建一个文件夹,命名为SYSNAP..把我们的程序sysnap.exe放进去
打开笔记本,那下面代码写进去,保存为sysnap.inf
[Version]
Signature="$WINDOWS NT$"
[DefaultInstall.Services]
AddService=sysnap,,My_AddService_Name
[My_AddService_Name]
DisplayName=sysnap
Description=显示电脑用户名
ServiceType=0x10
StartType=2
ErrorControl=0
ServiceBinary=C:/SYSNAP/sysnap.exe
 
说明一下
1、ServiceType服务类型:0x10为独立进程服务,0x20为共享进程服务(比如svchost)
2、StartType启动类型:0 系统引导时加载,1 OS初始化时加载,2 由SCM(服务控制管理器)自动启动,3 手动启动,4 禁用(注意,0和1只能用于驱动程序)
3、ErrorControl错误控制:0 忽略,1 继续并警告,2 切换到LastKnownGood的设置,3蓝屏
4、ServiceBinary服务程序位置
 
好了..在CMD下运行命令
 rundll32.exe setupapi,InstallHinfSection DefaultInstall 128 c:/SYSNAP/sysnap.inf 
这样就安装了一个名为sysnap的服务,是不是很简单呢..当然后.INF文件安装服务有时候是不太好,,,下面我们就通过编成来实现,,,也是主要的目的..先讲一下理论吧
 
3关于服务的一些基本理论知识
WIN32服务由三部分组成:服务应用程序、服务控制程序SCP,和服务控制管理器SCM。
服务应用程序:就是接下来我们要实现的程序,他是一个EXE文件..也可以是.DLL,这里我们是sysnap.exe
服务控制程序:控制服务应用程序的功能块,也是服务应用程序同服务管理器(SCM)之间的桥梁
服务控制管理器:负责加载和初始化AUTO_ATRT的服务程序,SCM维护着注册表中的服务数据库,位于:HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services。其下的子键就是安装的服务和驱动服务。每个子键的名称就是服务名,当安装的时候由服务安全程序的CreateService 函数指定
还有就不写了..可以参考<<inside windows 2000>>他们讲的已经足够好了...,不够里面涉及的东西很多.建议看下后面的程序...不懂的再去参考他们比较好..因为这个程序不要求你全要理解他们
 
4一个服务的创建流程一个服务的创建流程和基本组成
A编写我们的main()函数,该函数必须在30秒内调用StartServiceCtrlDispatcher 函数..这样我们的EXE文件就在SCM里注册了
 
B编写我们的ServiceMain(),ServiceMain()要立即调用RegisterServiceCtrlHandler 注册服务控制处理函数,然后用RegisterServiceCtrlHandler返回的句柄向SCM发送状态信息,接着开始完成实际的服务任务和工作线程,一旦线程开始,ServiceMain()就等待一个事件的发生,知道服务停止,ServiceMain()才返回
 
C编写我们的控制处理器ServiceCtrlHandler,接受来自SCM的请求并作出反应,其实请求一般是下面几个值(也可以自己定义)
停止服务:SERVICE_CONTROL_STOP 
暂停服务:SERVICE_CONTROL_PAUSE 
恢复被暂停的服务:SERVICE_CONTROL_CONTINUE 
返回服务的更新状态信息:SERVICE_CONTROL_INTERROGATE
 
D编写这个服务要实现的功能函数,也就是我们这个服务要完成什么功能,这里依然是输出电脑用户名
可见,一个完整的服务包括:
main():他告诉SCM 关于ServiceMain()的一些信息
ServiceMain():开始ServiceThread(),告诉SCM关于控制处理器的一些信息
ServiceCtrlHandler:接受来自SCM的请求并做出响应
InitThread():由ServiceMain()打开,执行我们的任务,,就是建立一个线程来运行我们的任务
 
5详说各个基本函数
A main()
SCM是一个管理系统所有服务的进程,当SCM启动某个服务时,它等待某个进程的主线程来调用StartServiceCtrlDispatcher(),这样把调用进程的住线程转换为控制分配器,控制分配器启动一个新线程,新线程运行分配表里每个服务的ServiceMain()
 
B ServiceMain()
是服务的入口点.它运行在一个单独的线程中,主要是为服务注册控制处理器,它指示控制分配器调用ServiceCtrlHandler()来处理SCM的请求,注册完成后将返回一个句柄
通过调用SetServiceStatus,用这个句柄和SERVICE_STATUS向SCM报告服务状态,,,因为这样的动作经常发生,,,所以我们把这个过程写成一个函数ReportStatusToSCMgr()
RegisterServiceCtrlHandler(strServiceName, (LPHANDLER_FUNCTION)ServiceCtrlHandler);
接着调用ReportStatusToSCMgr()向SCM报告服务状态 ReportStatusToSCMgr();
创建一个事件,在函数的最后将调用该事件来保持函数的运行知道SCM发出停止请求才返回 CreateEvent();
创建一个线程来运行我们的服务函数 sysnap();
 
最后ServiceThread()完成后返回ServiceMain(),ServiceMain()调用 WaitForSingleObject()
 
C ServiceCtrlHandler()
检查SCM发送了什么请求并且做出反应...当用户关闭系统,所有的控制处理要调用SetServiceStatus设置SERVICE_ACCEPT_SHUTDOWN控制码去接收SERVICE_CONTROL_SHUTDOWN控制码,如果服务需要时间去清除,它可以发送 STOP_PENDING状态消息,连同一个等待时间,这样,服务控制器在报告系统服务关闭之前才知道应该待多长时间,无论如何,都有一个服务控制器需要等待的时间,防止服务停留在shutdown状态。要改变这个时间限制,可以修改HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control中的WaitToKillServiceTimeout值。 
 
switch(nControlCode)
{
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
nServiceCurrentStatus=SERVICE_STOP_PENDING;
success=ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,0,1,3000);
KillService();
return;
default:
break;
}
 
6所用到的一些数据结构和API说明
本来想写,,算了.自己查MSDN///累....下面代码我会加点注射
 
下面就直接代码吧...可以自己编译
 
sv.exe的代码
 
#include <stdio.h>
#include <windows.h>
#include <winsvc.h>
//定义一些全局变量和函数
void ServiceMain(DWORD argc, LPTSTR *argv); 
void ServiceCtrlHandler(DWORD dwControlCode);
//SCM报告服务状态信息
BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode,
 DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint,
 DWORD dwWaitHint);
BOOL InitThread(); //创建线程来运行我们的任务
DWORD sysnap(LPDWORD param); //我们这个服务所要完成的任务
HANDLE hServiceThread; 
void KillService(); 
 
char *strServiceName = "sysnap"; ////标识服务的内部名
 
SERVICE_STATUS_HANDLE nServiceStatusHandle; //存储调用RegisterServiceCtrlHandler返回的句柄
HANDLE killServiceEvent;
BOOL nServiceRunning;
DWORD nServiceCurrentStatus;
 
void main(int argc, char* argv[])
{
// SERVICE_TABLE_ENTRY 结构类型的数组,他包含了调用进程所提供的每个服务的入口函数和字符串名。表中的最后一个元素必须为 NULL,指明入口表结束
SERVICE_TABLE_ENTRY servicetable[]=
{
{strServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},
{NULL,NULL}
};
BOOL success;
 // StartServiceCtrlDispatcher 函数负责把程序主线程连接到服务控制管理程序
success=StartServiceCtrlDispatcher(servicetable);
if(!success)
{
printf("fialed!");
}
}
 
void ServiceMain(DWORD argc, LPTSTR *argv)
{
BOOL success;
//把ServiceCtrlHandler注册为服务控制器,接受来自SCM的请求并做出处理,
nServiceStatusHandle=RegisterServiceCtrlHandler(strServiceName,
(LPHANDLER_FUNCTION)ServiceCtrlHandler);
//判断是否注册成功,否则返回
if(!nServiceStatusHandle)
{
return;
}
//注册成功后向SCM报告服务状态信息,因为服务还没初始化完成,所以当前服务状态为SERVICE_START_PENDING
success=ReportStatusToSCMgr(SERVICE_START_PENDING,NO_ERROR,0,1,3000);
if(!success)
{
return;
}
//创建一个事件,在函数的最后将用该事件来保持函数的运行直到SCM发出停止请求才返回
killServiceEvent=CreateEvent(0,TRUE,FALSE,0);
if(killServiceEvent==NULL)
{
return;
}
//向SCM报告服务状态信息
success=ReportStatusToSCMgr(SERVICE_START_PENDING,NO_ERROR,0,2,1000);
if(!success)
{
return;
}
//InitThread()创建一个线程来运行我们的sysnap()函数
success=InitThread();
if(!success)
{
return;
}
//我们的服务开始运行任务了,当前状态设置为SERVICE_RUNNING
nServiceCurrentStatus=SERVICE_RUNNING;
success=ReportStatusToSCMgr(SERVICE_RUNNING,NO_ERROR,0,0,0);
if(!success)
{
return;
}
//sysnap()函数运行完了之后返回ServiceMain(),ServiceMain()调用WaitForSingleObject,因为服务被停止之前ServiceMain()不会结束
WaitForSingleObject(killServiceEvent,INFINITE);
CloseHandle(killServiceEvent);
}
 
 
//向SCM报告服务状态信息,可以说是更新信息吧,它接受的参数都是SERVICE_STATUS结构成员
BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode,
 DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint,
 DWORD dwWaitHint)
{
BOOL success;
SERVICE_STATUS nServiceStatus; //定义一个SERVICE_STATUS类型结构nServiceStatus
nServiceStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS; //表示我们的服务是独占一个进程的服务
nServiceStatus.dwCurrentState=dwCurrentState; //当前服务状态
//
if(dwCurrentState==SERVICE_START_PENDING) 
{
nServiceStatus.dwControlsAccepted=0; //服务的初始化没有完成
}
else
{
nServiceStatus.dwControlsAccepted=SERVICE_ACCEPT_STOP //通知 SCM 服务接受哪个域。这里允许 STOP 和 SHUTDOWN 请求
|SERVICE_ACCEPT_SHUTDOWN;
}
//dwServiceSpecificExitCode在你终止服务并报告退出细节时很有用。初始化服务时并不退出,因此值为 0
if(dwServiceSpecificExitCode==0)
{
nServiceStatus.dwWin32ExitCode=dwWin32ExitCode;
}
else
{
nServiceStatus.dwWin32ExitCode=ERROR_SERVICE_SPECIFIC_ERROR;
}
nServiceStatus.dwServiceSpecificExitCode=dwServiceSpecificExitCode;
//
nServiceStatus.dwCheckPoint=dwCheckPoint;
nServiceStatus.dwWaitHint=dwWaitHint;
 //设置好nServiceStatus后,向SCM报告服务状态
 success=SetServiceStatus(nServiceStatusHandle,&nServiceStatus);
 
if(!success)
{
KillService();
return success;
}
else
return success;
}
 
BOOL InitThread()
{
DWORD id;
hServiceThread=CreateThread(0,0,
(LPTHREAD_START_ROUTINE)sysnap,
0,0,&id);
if(hServiceThread==0)
{
return false;
}
else
{
nServiceRunning=true;
return true;
}
}
 
DWORD sysnap(LPDWORD param)
{
 while(nServiceRunning)
{
Beep(450,150);
Sleep(4000);
}
 
return 0;
}
 
void KillService()
{
nServiceRunning=false;
SetEvent(killServiceEvent);
ReportStatusToSCMgr(SERVICE_STOPPED,NO_ERROR,0,0,0);
}
 
void ServiceCtrlHandler(DWORD dwControlCode)
{
BOOL success;
switch(dwControlCode)
{
case SERVICE_CONTROL_SHUTDOWN: 
case SERVICE_CONTROL_STOP:
nServiceCurrentStatus=SERVICE_STOP_PENDING;
success=ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,0,1,3000);//先更新服务状态为 SERVICDE_STOP_PENDING,再停止服务
KillService();
return;
default:
break;
}
ReportStatusToSCMgr(nServiceCurrentStatus,NO_ERROR,0,0,0);
}
 
svload.exe的代码
#include <stdio.h>
#include <windows.h>
#include <winsvc.h>
 
int main(int argc, char* argv[])
{
 char* showInfo="sysnap's first Windows service";      //注意宽字符的转换(L)
 char* showName="sysnap";
 char* sv_Path="C://sysnap//sv.exe";
 
SC_HANDLE Hsysnap;
 SC_HANDLE hSCManager;
 
hSCManager=OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
if(!hSCManager)
{
printf("failed");
return 1;
}
 
Hsysnap=CreateService(hSCManager,TEXT(showName),
TEXT(showInfo),
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
sv_Path,
0,0,0,0,0);
if(!Hsysnap)
{
CloseServiceHandle(hSCManager);
printf("failed");
return 1;
}
CloseServiceHandle(Hsysnap);
CloseServiceHandle(hSCManager);
return 0;
}
 
俩个EXE文件一共334K....如果不想编译我可以把他们发到你邮葙
 
 
哈哈..谢谢给个精品..当时在写的时候认为很清晰..不过现在自己再看一遍..是比较长..我把他的整体弄上来,,,看了才不灰乱

void main()
{
调用StartServiceCtrlDispatcher(),把控制交给控制分配器,控制分配器新建一个线程来运行ServiceMain..也就是真正进入服务
}

void ServiceMain(DWORD argc, LPTSTR *argv)
{

RegisterServiceCtrlHandler注册为服务控制器,接受来自SCM的请求并做出处理

创建一个事件,在函数的最后将用该事件来保持函数的运行直到SCM发出停止请求 才返回

创建一个线程来运行我们的函数

函数运行完了之后返回ServiceMain(),ServiceMain()调用 
}

BOOL ReportStatusToSCMgr()
{
 填充SERVICE_STATUS 成员..并把它做为参数传给etServiceStatus()向SCM报告服务状 态 

}

BOOL InitThread()
{
创建运行sysnap()的线程
}

DWORD sysnap(LPDWORD param)
{
 我们要做的工作
}

void KillService()
{
停止服务
}

void ServiceCtrlHandler(DWORD dwControlCode)
{
接受来自SCM的请求并做出反应..这里要自己实现几个函数;
}
 
 
总之记住看的时候以main(),ServiceMain()和ServiceCtrlHandler()为中心..其他函数都是比较简单的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息