标题: MSDN系列(8)--学习写一个Hello World服务(Service)
创建: 2003-12-06 15:51 更新: 2003-12-09 16:32 链接: http://scz.617.cn/windows/200312091659.txt
目录:
☆ 学习背景
☆ helloservice.c
☆ installservice.c
☆ startservice.c
☆ uninstallservice.c
☆ beepservice.c
☆ Service相关操作
☆ Service相关的注册表内容
☆ 初次接触Service时的一点感性认识
☆ 参考资源
☆ 学习背景
在Unix下,我们有daemon可用,有fork()可用,有&可用,在Windows下,用Service。 在需要稳定、可靠的SYSTEM权限时,用Service。
一直没有接触过Service,直到前些天打算看看pwdump3的源代码,迫不得已,走上学 习之路。事实上这半年写过的Windows程序,80%为了抓包进行Network Hacking,10% 为了理解Windows系统本身,剩下10%纯粹为了攻击(这个动机并不高尚,还好我不是 高尚的人)。
所附代码源自[1]、[2],如不放心我的C语言,请自行参考原作。从Win32程序员角度 看,程序必有很多问题。第一因我水平有限,改不好。第二因我不想为此付出更多精 力,不想改。第三我的动机比较简单,试着初步理解Service,并能读懂利用Service 原理的代码,为以后学习调试Service做点铺垫,从这个角度来讲,已经达到目的了。
☆ helloservice.c
/*
- Version : 1.00
- Compile : For x86/EWindows XP SP1 & VC 7
-
: cl helloservice.c /nologo /Os /G6 /Gs65536 /W3 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /link /RELEASE
-
:
- Create : 2003-12-06 16:28
- Modify : 2003-12-09 15:41 */
/************************************************************************
-
*
-
Head File *
-
*
************************************************************************/
#define _WIN32_WINNT 0x0501
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h>
/************************************************************************
-
*
-
Macro *
-
*
************************************************************************/
#pragma comment( linker, "/INCREMENTAL:NO" ) #pragma comment( linker, "/subsystem:console" ) #pragma comment( lib, "kernel32.lib" ) #pragma comment( lib, "user32.lib" ) #pragma comment( lib, "advapi32.lib" )
#define VERSION "1.00"
#define SERVICERUNNING 0x00000001 #define SERVICEPAUSED 0x00000002 #define MINFREQUENCY 0x25 #define MAXFREQUENCY 0x7FFF #define DEFAULTFREQUENCY 200 #define DEFAULTDURATION 200 #define MININTERVAL 1000 #define MAXINTERVAL 3600000 #define DEFAULTINTERVAL 2000 #define SERVICE_CONTROL_PRIVATE_128 128
typedef struct _HELLOWORLDPARAM { DWORD frequency; DWORD duration; DWORD interval; } HELLOWORLDPARAM, *PHELLOWORLDPARAM;
/************************************************************************
-
*
-
Function Prototype *
-
*
************************************************************************/
static VOID WINAPI HelloWorld_Handler ( DWORD fdwControl // requested control code ); static VOID WINAPI HelloWorld_ServiceMain ( DWORD argc, // number of arguments LPTSTR *argv // array of arguments ); static DWORD WINAPI HelloWorld_Thread ( LPVOID lpParameter ); static DWORD HelloWorld_UpdateStatus ( DWORD CurrentState, DWORD ControlsAccepted, DWORD Win32ExitCode, DWORD ServiceSpecificExitCode, DWORD CheckPoint, DWORD WaitHint ); static void PrintWin32ErrorGUI ( char *message, DWORD dwMessageId );
/************************************************************************
-
*
-
Static Global Var *
-
*
************************************************************************/
static unsigned char HelloWorld_service_name[] = "HelloWorld"; static SERVICE_STATUS_HANDLE HelloWorld_handle = ( SERVICE_STATUS_HANDLE )0; static volatile HANDLE HelloWorld_TerminateEvent = NULL; static volatile HANDLE HelloWorld_thread = NULL; static volatile DWORD HelloWorld_ServiceStatus = 0; static volatile DWORD HelloWorld_frequency = DEFAULTFREQUENCY; static volatile DWORD HelloWorld_duration = DEFAULTDURATION; static DWORD HelloWorld_interval = DEFAULTINTERVAL;
/************************************************************************/
static VOID WINAPI HelloWorld_Handler ( DWORD fdwControl // requested control code ) { switch ( fdwControl ) { case SERVICE_CONTROL_CONTINUE: if ( ( HelloWorld_ServiceStatus & ( SERVICERUNNING | SERVICEPAUSED ) ) == ( SERVICERUNNING | SERVICEPAUSED ) ) { HelloWorld_UpdateStatus ( SERVICE_CONTINUE_PENDING, 0, NO_ERROR, 0, 1, 5000 ); if ( NULL != HelloWorld_thread ) { ResumeThread( HelloWorld_thread ); } HelloWorld_UpdateStatus ( SERVICE_RUNNING, SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP, NO_ERROR, 0, 0, 0 ); HelloWorld_ServiceStatus = SERVICERUNNING; } break; case SERVICE_CONTROL_INTERROGATE: if ( ( HelloWorld_ServiceStatus & ( SERVICERUNNING | SERVICEPAUSED ) ) == ( SERVICERUNNING | SERVICEPAUSED ) ) { HelloWorld_UpdateStatus ( SERVICE_PAUSED, SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP, NO_ERROR, 0, 0, 0 ); } else if ( SERVICERUNNING == HelloWorld_ServiceStatus ) { HelloWorld_UpdateStatus ( SERVICE_RUNNING, SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP, NO_ERROR, 0, 0, 0 ); } break; case SERVICE_CONTROL_PAUSE: if ( SERVICERUNNING == HelloWorld_ServiceStatus ) { HelloWorld_UpdateStatus ( SERVICE_PAUSE_PENDING, 0, NO_ERROR, 0, 1, 5000 ); if ( NULL != HelloWorld_thread ) { SuspendThread( HelloWorld_thread ); } HelloWorld_UpdateStatus ( SERVICE_PAUSED, SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP, NO_ERROR, 0, 0, 0 ); HelloWorld_ServiceStatus |= SERVICEPAUSED; } break; case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: if ( 0 != HelloWorld_ServiceStatus ) { HelloWorld_UpdateStatus ( SERVICE_STOP_PENDING, SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP, NO_ERROR, 0, 1, 5000 ); } HelloWorld_ServiceStatus = 0; if ( NULL != HelloWorld_thread ) { ResumeThread( HelloWorld_thread ); } if ( NULL != HelloWorld_TerminateEvent ) { SetEvent( HelloWorld_TerminateEvent ); } break; case SERVICE_CONTROL_PRIVATE_128: Beep( HelloWorld_frequency * 2, HelloWorld_duration * 2 ); break; default: break; } /* end of switch / return; } / end of HelloWorld_Handler */
static VOID WINAPI HelloWorld_ServiceMain ( DWORD argc, // number of arguments LPTSTR *argv // array of arguments ) { HELLOWORLDPARAM HelloWorld_Param; DWORD error, c;
HelloWorld_handle = RegisterServiceCtrlHandler
(
HelloWorld_service_name,
( LPHANDLER_FUNCTION )HelloWorld_Handler
);
if ( !HelloWorld_handle )
{
error = GetLastError();
goto HelloWorld_ServiceMain_exit;
}
error = HelloWorld_UpdateStatus
(
SERVICE_START_PENDING,
0,
NO_ERROR,
0,
1,
5000
);
if ( ERROR_SUCCESS != error )
{
goto HelloWorld_ServiceMain_exit;
}
HelloWorld_TerminateEvent = CreateEvent
(
NULL,
TRUE,
FALSE,
NULL
);
if ( NULL == HelloWorld_TerminateEvent )
{
error = GetLastError();
goto HelloWorld_ServiceMain_exit;
}
error = HelloWorld_UpdateStatus
(
SERVICE_START_PENDING,
0,
NO_ERROR,
0,
2,
5000
);
if ( ERROR_SUCCESS != error )
{
goto HelloWorld_ServiceMain_exit;
}
for ( c = 1; c < argc; c++ )
{
if ( ( ( argv[c][0] != '-' ) && ( argv[c][0] != '/' ) ) || ( strlen( argv[c] ) < 2 ) )
{
goto HelloWorld_ServiceMain_0;
}
else
{
switch ( tolower( argv[c][1] ) )
{
case 'd':
if ( ( c + 1 ) >= argc )
{
goto HelloWorld_ServiceMain_0;
}
HelloWorld_duration = ( DWORD )strtoul( argv[++c], NULL, 0 );
break;
case 'f':
if ( ( c + 1 ) >= argc )
{
goto HelloWorld_ServiceMain_0;
}
HelloWorld_frequency = ( DWORD )strtoul( argv[++c], NULL, 0 );
break;
case 'i':
if ( ( c + 1 ) >= argc )
{
goto HelloWorld_ServiceMain_0;
}
HelloWorld_interval = ( DWORD )strtoul( argv[++c], NULL, 0 );
break;
default:
goto HelloWorld_ServiceMain_0;
break;
} /* end of switch */
}
} /* end of for */
HelloWorld_ServiceMain_0:
if ( HelloWorld_frequency < MINFREQUENCY )
{
HelloWorld_frequency = MINFREQUENCY;
}
else if ( HelloWorld_frequency > MAXFREQUENCY )
{
HelloWorld_frequency = MAXFREQUENCY;
}
if ( HelloWorld_interval < MININTERVAL )
{
HelloWorld_interval = MININTERVAL;
}
else if ( HelloWorld_interval > MAXINTERVAL )
{
HelloWorld_interval = MAXINTERVAL;
}
error = HelloWorld_UpdateStatus
(
SERVICE_START_PENDING,
0,
NO_ERROR,
0,
3,
5000
);
if ( ERROR_SUCCESS != error )
{
goto HelloWorld_ServiceMain_exit;
}
HelloWorld_Param.frequency = HelloWorld_frequency;
HelloWorld_Param.duration = HelloWorld_duration;
HelloWorld_Param.interval = HelloWorld_interval;
HelloWorld_thread = CreateThread
(
NULL,
0,
( LPTHREAD_START_ROUTINE )HelloWorld_Thread,
&HelloWorld_Param,
CREATE_SUSPENDED,
NULL
);
if ( NULL == HelloWorld_thread )
{
error = GetLastError();
goto HelloWorld_ServiceMain_exit;
}
HelloWorld_ServiceStatus = SERVICERUNNING;
ResumeThread( HelloWorld_thread );
error = HelloWorld_UpdateStatus
(
SERVICE_RUNNING,
SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP,
NO_ERROR,
0,
0,
0
);
if ( ERROR_SUCCESS != error )
{
goto HelloWorld_ServiceMain_exit;
}
WaitForSingleObject( HelloWorld_TerminateEvent, INFINITE );
error = NO_ERROR;
HelloWorld_ServiceMain_exit:
if ( 0 != HelloWorld_handle )
{
HelloWorld_UpdateStatus
(
SERVICE_STOPPED,
0,
error,
0,
0,
0
);
HelloWorld_handle = ( SERVICE_STATUS_HANDLE )0;
}
HelloWorld_ServiceStatus = 0;
HelloWorld_Param.interval = 0;
if ( NULL != HelloWorld_thread )
{
ResumeThread( HelloWorld_thread );
CloseHandle( HelloWorld_thread );
HelloWorld_thread = NULL;
}
if ( NULL != HelloWorld_TerminateEvent )
{
CloseHandle( HelloWorld_TerminateEvent );
HelloWorld_TerminateEvent = NULL;
}
return;
} /* end of HelloWorld_ServiceMain */
static DWORD WINAPI HelloWorld_Thread ( LPVOID lpParameter ) { while ( 0 != HelloWorld_ServiceStatus ) { Beep ( ( ( PHELLOWORLDPARAM )lpParameter )->frequency, ( ( PHELLOWORLDPARAM )lpParameter )->duration ); Sleep ( ( ( PHELLOWORLDPARAM )lpParameter )->interval ); } /* end of while / return( 0 ); } / end of HelloWorld_Thread */
static DWORD HelloWorld_UpdateStatus ( DWORD CurrentState, DWORD ControlsAccepted, DWORD Win32ExitCode, DWORD ServiceSpecificExitCode, DWORD CheckPoint, DWORD WaitHint ) { SERVICE_STATUS service_status; DWORD error = ERROR_SUCCESS;
service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
service_status.dwCurrentState = CurrentState;
service_status.dwControlsAccepted = ControlsAccepted;
service_status.dwWin32ExitCode = Win32ExitCode;
service_status.dwServiceSpecificExitCode = ServiceSpecificExitCode;
service_status.dwCheckPoint = CheckPoint;
service_status.dwWaitHint = WaitHint;
if ( FALSE == SetServiceStatus( HelloWorld_handle, &service_status ) )
{
error = GetLastError();
if ( NULL != HelloWorld_TerminateEvent )
{
SetEvent( HelloWorld_TerminateEvent );
}
goto HelloWorld_UpdateStatus_exit;
}
HelloWorld_UpdateStatus_exit:
return( error );
} /* end of HelloWorld_UpdateStatus */
static void PrintWin32ErrorGUI ( char *message, DWORD dwMessageId ) { char *errMsg;
FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwMessageId,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
( LPTSTR )&errMsg,
0,
NULL
);
MessageBox( NULL, errMsg, message, MB_OK | MB_SERVICE_NOTIFICATION );
LocalFree( errMsg );
return;
} /* end of PrintWin32ErrorGUI */
int __cdecl main ( int argc, char * argv[] ) { int ret = EXIT_FAILURE; SERVICE_TABLE_ENTRY service_table[] = { { HelloWorld_service_name, ( LPSERVICE_MAIN_FUNCTION )HelloWorld_ServiceMain }, { NULL, NULL } };
if ( FALSE == StartServiceCtrlDispatcher( service_table ) )
{
PrintWin32ErrorGUI( "StartServiceCtrlDispatcher() failed", GetLastError() );
goto main_exit;
}
ret = EXIT_SUCCESS;
main_exit:
return( ret );
} /* end of main */
/************************************************************************/
☆ installservice.c
/*
- Version : 1.00
- Compile : For x86/EWindows XP SP1 & VC 7
-
: cl installservice.c /nologo /Os /G6 /Gs65536 /W3 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /link /RELEASE
-
:
- Create : 2003-12-08 16:47
- Modify : 2003-12-09 14:54 */
/************************************************************************
-
*
-
Head File *
-
*
************************************************************************/
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h>
/************************************************************************
-
*
-
Macro *
-
*
************************************************************************/
#pragma comment( linker, "/INCREMENTAL:NO" ) #pragma comment( linker, "/subsystem:console" ) #pragma comment( lib, "kernel32.lib" ) #pragma comment( lib, "advapi32.lib" )
#define VERSION "1.00"
/************************************************************************
-
*
-
Function Prototype *
-
*
************************************************************************/
static void PrintWin32ErrorCUI ( char *message, DWORD dwMessageId ); static void usage ( char *arg );
/************************************************************************
-
*
-
Static Global Var *
-
*
************************************************************************/
/************************************************************************/
static void PrintWin32ErrorCUI ( char *message, DWORD dwMessageId ) { char *errMsg;
FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwMessageId,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
( LPTSTR )&errMsg,
0,
NULL
);
fprintf( stderr, "%s: %s", message, errMsg );
LocalFree( errMsg );
return;
} /* end of PrintWin32ErrorCUI */
static void usage ( char arg ) { fprintf ( stderr, "Usage: %s [-h] [-v] [-t target] [-s servicename] [-d displayname] [-c cmdline]\n", arg ); exit( EXIT_FAILURE ); } / end of usage */
int __cdecl main ( int argc, char * argv[] ) { SC_HANDLE scm = ( SC_HANDLE )NULL, sc_handle = ( SC_HANDLE )NULL; unsigned char *target = NULL, *servicename = NULL, *displayname = NULL, *cmdline = NULL; int c, ret = EXIT_FAILURE;
if ( 1 == argc )
{
usage( argv[0] );
}
for ( c = 1; c < argc; c++ )
{
if ( ( ( argv[c][0] != '-' ) && ( argv[c][0] != '/' ) ) || ( strlen( argv[c] ) < 2 ) )
{
usage( argv[0] );
}
else
{
switch ( tolower( argv[c][1] ) )
{
case 'c':
if ( ( c + 1 ) >= argc )
{
usage( argv[0] );
}
cmdline = argv[++c];
break;
case 'd':
if ( ( c + 1 ) >= argc )
{
usage( argv[0] );
}
displayname = argv[++c];
break;
case 's':
if ( ( c + 1 ) >= argc )
{
usage( argv[0] );
}
servicename = argv[++c];
break;
case 't':
if ( ( c + 1 ) >= argc )
{
usage( argv[0] );
}
target = argv[++c];
break;
case 'v':
fprintf( stderr, "%s ver "VERSION"\n", argv[0] );
return( EXIT_SUCCESS );
case 'h':
case '?':
default:
usage( argv[0] );
break;
} /* end of switch */
}
} /* end of for */
if ( NULL == servicename )
{
fprintf( stderr, "Checking your [-s servicename]\n" );
return( EXIT_FAILURE );
}
if ( NULL == displayname )
{
fprintf( stderr, "Checking your [-d displayname]\n" );
return( EXIT_FAILURE );
}
if ( NULL == cmdline )
{
fprintf( stderr, "Checking your [-c cmdline]\n" );
return( EXIT_FAILURE );
}
scm = OpenSCManager
(
target,
SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CREATE_SERVICE
);
if ( NULL == scm )
{
PrintWin32ErrorCUI( "OpenSCManager() failed", GetLastError() );
goto main_exit;
}
sc_handle = CreateService
(
scm,
servicename,
displayname,
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
cmdline,
NULL,
NULL,
NULL,
NULL,
NULL
);
if ( NULL == sc_handle )
{
PrintWin32ErrorCUI( "CreateService() failed", GetLastError() );
goto main_exit;
}
ret = EXIT_SUCCESS;
main_exit:
if ( NULL != sc_handle )
{
CloseServiceHandle( sc_handle );
sc_handle = ( SC_HANDLE )NULL;
}
if ( NULL != scm )
{
CloseServiceHandle( scm );
scm = ( SC_HANDLE )NULL;
}
return( ret );
} /* end of main */
/************************************************************************/
☆ startservice.c
/*
- Version : 1.00
- Compile : For x86/EWindows XP SP1 & VC 7
-
: cl startservice.c /nologo /Os /G6 /Gs65536 /W3 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /link /RELEASE
-
:
- Create : 2003-12-08 22:23
- Modify : 2003-12-09 15:47 */
/************************************************************************
-
*
-
Head File *
-
*
************************************************************************/
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h>
/************************************************************************
-
*
-
Macro *
-
*
************************************************************************/
#pragma comment( linker, "/INCREMENTAL:NO" ) #pragma comment( linker, "/subsystem:console" ) #pragma comment( lib, "kernel32.lib" ) #pragma comment( lib, "advapi32.lib" )
#define VERSION "1.00"
/************************************************************************
-
*
-
Function Prototype *
-
*
************************************************************************/
static void PrintWin32ErrorCUI ( char *message, DWORD dwMessageId ); static void usage ( char *arg );
/************************************************************************
-
*
-
Static Global Var *
-
*
************************************************************************/
/************************************************************************/
static void PrintWin32ErrorCUI ( char *message, DWORD dwMessageId ) { char *errMsg;
FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwMessageId,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
( LPTSTR )&errMsg,
0,
NULL
);
fprintf( stderr, "%s: %s", message, errMsg );
LocalFree( errMsg );
return;
} /* end of PrintWin32ErrorCUI */
static void usage ( char arg ) { fprintf ( stderr, "Usage: %s [-h] [-v] [-t target] [-s servicename] ...\n", arg ); exit( EXIT_FAILURE ); } / end of usage */
int __cdecl main ( int argc, char * argv[] ) { SC_HANDLE scm = ( SC_HANDLE )NULL, sc_handle = ( SC_HANDLE )NULL; unsigned char *target = NULL, *servicename = NULL; int c, ret = EXIT_FAILURE; DWORD sargc = 0; unsigned char **sargv = NULL;
if ( 1 == argc )
{
usage( argv[0] );
}
for ( c = 1; c < argc; c++ )
{
if ( ( ( argv[c][0] != '-' ) && ( argv[c][0] != '/' ) ) || ( strlen( argv[c] ) < 2 ) )
{
usage( argv[0] );
}
else
{
switch ( tolower( argv[c][1] ) )
{
case 's':
if ( ( c + 1 ) >= argc )
{
usage( argv[0] );
}
servicename = argv[++c];
goto main_0;
case 't':
if ( ( c + 1 ) >= argc )
{
usage( argv[0] );
}
target = argv[++c];
break;
case 'v':
fprintf( stderr, "%s ver "VERSION"\n", argv[0] );
return( EXIT_SUCCESS );
case 'h':
case '?':
default:
usage( argv[0] );
break;
} /* end of switch */
}
} /* end of for */
main_0:
c++;
if ( c < argc )
{
sargc = argc - c;
sargv = &argv[c];
printf
(
"argc = %d\n"
"c = %d\n"
"sargc = %u\n"
"sargv[0] = %s\n",
argc,
c,
sargc,
sargv[0]
);
}
if ( NULL == servicename )
{
fprintf( stderr, "Checking your [-s servicename]\n" );
return( EXIT_FAILURE );
}
scm = OpenSCManager
(
target,
SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CONNECT
);
if ( NULL == scm )
{
PrintWin32ErrorCUI( "OpenSCManager() failed", GetLastError() );
goto main_exit;
}
sc_handle = OpenService
(
scm,
servicename,
SERVICE_START
);
if ( NULL == sc_handle )
{
PrintWin32ErrorCUI( "OpenService() failed", GetLastError() );
goto main_exit;
}
if ( FALSE == StartService
(
sc_handle,
sargc,
sargv
) )
{
PrintWin32ErrorCUI( "StartService() failed", GetLastError() );
goto main_exit;
}
ret = EXIT_SUCCESS;
main_exit:
if ( NULL != sc_handle )
{
CloseServiceHandle( sc_handle );
sc_handle = ( SC_HANDLE )NULL;
}
if ( NULL != scm )
{
CloseServiceHandle( scm );
scm = ( SC_HANDLE )NULL;
}
return( ret );
} /* end of main */
/************************************************************************/
☆ uninstallservice.c
/*
- Version : 1.00
- Compile : For x86/EWindows XP SP1 & VC 7
-
: cl uninstallservice.c /nologo /Os /G6 /Gs65536 /W3 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /link /RELEASE
-
:
- Create : 2003-12-08 17:37
- Modify : 2003-12-08 22:14 */
/************************************************************************
-
*
-
Head File *
-
*
************************************************************************/
/*
- #define _WIN32_WINNT 0x0501 */
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h>
/************************************************************************
-
*
-
Macro *
-
*
************************************************************************/
#pragma comment( linker, "/INCREMENTAL:NO" ) #pragma comment( linker, "/subsystem:console" ) #pragma comment( lib, "kernel32.lib" ) #pragma comment( lib, "advapi32.lib" )
#define VERSION "1.00"
/************************************************************************
-
*
-
Function Prototype *
-
*
************************************************************************/
static void PrintWin32ErrorCUI ( char *message, DWORD dwMessageId ); static void usage ( char *arg );
/************************************************************************
-
*
-
Static Global Var *
-
*
************************************************************************/
/************************************************************************/
static void PrintWin32ErrorCUI ( char *message, DWORD dwMessageId ) { char *errMsg;
FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwMessageId,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
( LPTSTR )&errMsg,
0,
NULL
);
fprintf( stderr, "%s: %s", message, errMsg );
LocalFree( errMsg );
return;
} /* end of PrintWin32ErrorCUI */
static void usage ( char arg ) { fprintf ( stderr, "Usage: %s [-h] [-v] [-t target] [-s servicename]\n", arg ); exit( EXIT_FAILURE ); } / end of usage */
int __cdecl main ( int argc, char * argv[] ) { SC_HANDLE scm = ( SC_HANDLE )NULL, sc_handle = ( SC_HANDLE )NULL; SERVICE_STATUS status; unsigned char *target = NULL, *servicename = NULL; int c, ret = EXIT_FAILURE;
if ( 1 == argc )
{
usage( argv[0] );
}
for ( c = 1; c < argc; c++ )
{
if ( ( ( argv[c][0] != '-' ) && ( argv[c][0] != '/' ) ) || ( strlen( argv[c] ) < 2 ) )
{
usage( argv[0] );
}
else
{
switch ( tolower( argv[c][1] ) )
{
case 's':
if ( ( c + 1 ) >= argc )
{
usage( argv[0] );
}
servicename = argv[++c];
break;
case 't':
if ( ( c + 1 ) >= argc )
{
usage( argv[0] );
}
target = argv[++c];
break;
case 'v':
fprintf( stderr, "%s ver "VERSION"\n", argv[0] );
return( EXIT_SUCCESS );
case 'h':
case '?':
default:
usage( argv[0] );
break;
} /* end of switch */
}
} /* end of for */
if ( NULL == servicename )
{
fprintf( stderr, "Checking your [-s servicename]\n" );
return( EXIT_FAILURE );
}
scm = OpenSCManager
(
target,
SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CONNECT
);
if ( NULL == scm )
{
PrintWin32ErrorCUI( "OpenSCManager() failed", GetLastError() );
goto main_exit;
}
sc_handle = OpenService
(
scm,
servicename,
SERVICE_ALL_ACCESS
);
if ( NULL == sc_handle )
{
PrintWin32ErrorCUI( "OpenService() failed", GetLastError() );
goto main_exit;
}
if ( FALSE == ControlService
(
sc_handle,
SERVICE_CONTROL_INTERROGATE,
&status
) )
{
PrintWin32ErrorCUI( "ControlService( SERVICE_CONTROL_INTERROGATE ) failed", GetLastError() );
}
if ( FALSE == QueryServiceStatus( sc_handle, &status ) )
{
PrintWin32ErrorCUI( "QueryServiceStatus() failed", GetLastError() );
goto main_exit;
}
if ( SERVICE_STOPPED != status.dwCurrentState )
{
printf( "Stopping service ... ...\n" );
if ( FALSE == ControlService
(
sc_handle,
SERVICE_CONTROL_STOP,
&status
) )
{
PrintWin32ErrorCUI( "ControlService( SERVICE_CONTROL_STOP ) failed", GetLastError() );
}
Sleep( 500 );
}
if ( FALSE == DeleteService( sc_handle ) )
{
PrintWin32ErrorCUI( "DeleteService() failed", GetLastError() );
goto main_exit;
}
printf( "Ok\n" );
ret = EXIT_SUCCESS;
main_exit:
if ( NULL != sc_handle )
{
CloseServiceHandle( sc_handle );
sc_handle = ( SC_HANDLE )NULL;
}
if ( NULL != scm )
{
CloseServiceHandle( scm );
scm = ( SC_HANDLE )NULL;
}
return( ret );
} /* end of main */
/************************************************************************/
☆ beepservice.c
/*
- Version : 1.00
- Compile : For x86/EWindows XP SP1 & VC 7
-
: cl beepservice.c /nologo /Os /G6 /Gs65536 /W3 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /link /RELEASE
-
:
- Create : 2003-12-08 20:33
- Modify : 2003-12-08 22:05 */
/************************************************************************
-
*
-
Head File *
-
*
************************************************************************/
/*
- #define _WIN32_WINNT 0x0501 */
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h>
/************************************************************************
-
*
-
Macro *
-
*
************************************************************************/
#pragma comment( linker, "/INCREMENTAL:NO" ) #pragma comment( linker, "/subsystem:console" ) #pragma comment( lib, "kernel32.lib" ) #pragma comment( lib, "advapi32.lib" )
#define VERSION "1.00" #define SERVICE_CONTROL_PRIVATE_128 128
/************************************************************************
-
*
-
Function Prototype *
-
*
************************************************************************/
static void PrintWin32ErrorCUI ( char *message, DWORD dwMessageId ); static void usage ( char *arg );
/************************************************************************
-
*
-
Static Global Var *
-
*
************************************************************************/
/************************************************************************/
static void PrintWin32ErrorCUI ( char *message, DWORD dwMessageId ) { char *errMsg;
FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwMessageId,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
( LPTSTR )&errMsg,
0,
NULL
);
fprintf( stderr, "%s: %s", message, errMsg );
LocalFree( errMsg );
return;
} /* end of PrintWin32ErrorCUI */
static void usage ( char arg ) { fprintf ( stderr, "Usage: %s [-h] [-v] [-t target] [-s servicename]\n", arg ); exit( EXIT_FAILURE ); } / end of usage */
int __cdecl main ( int argc, char * argv[] ) { SC_HANDLE scm = ( SC_HANDLE )NULL, sc_handle = ( SC_HANDLE )NULL; SERVICE_STATUS status; unsigned char *target = NULL, *servicename = NULL; int c, ret = EXIT_FAILURE;
if ( 1 == argc )
{
usage( argv[0] );
}
for ( c = 1; c < argc; c++ )
{
if ( ( ( argv[c][0] != '-' ) && ( argv[c][0] != '/' ) ) || ( strlen( argv[c] ) < 2 ) )
{
usage( argv[0] );
}
else
{
switch ( tolower( argv[c][1] ) )
{
case 's':
if ( ( c + 1 ) >= argc )
{
usage( argv[0] );
}
servicename = argv[++c];
break;
case 't':
if ( ( c + 1 ) >= argc )
{
usage( argv[0] );
}
target = argv[++c];
break;
case 'v':
fprintf( stderr, "%s ver "VERSION"\n", argv[0] );
return( EXIT_SUCCESS );
case 'h':
case '?':
default:
usage( argv[0] );
break;
} /* end of switch */
}
} /* end of for */
if ( NULL == servicename )
{
fprintf( stderr, "Checking your [-s servicename]\n" );
return( EXIT_FAILURE );
}
scm = OpenSCManager
(
target,
SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CONNECT
);
if ( NULL == scm )
{
PrintWin32ErrorCUI( "OpenSCManager() failed", GetLastError() );
goto main_exit;
}
sc_handle = OpenService
(
scm,
servicename,
SERVICE_USER_DEFINED_CONTROL
);
if ( NULL == sc_handle )
{
PrintWin32ErrorCUI( "OpenService() failed", GetLastError() );
goto main_exit;
}
if ( FALSE == ControlService
(
sc_handle,
SERVICE_CONTROL_PRIVATE_128,
&status
) )
{
PrintWin32ErrorCUI( "ControlService( SERVICE_CONTROL_PRIVATE_128 ) failed", GetLastError() );
}
ret = EXIT_SUCCESS;
main_exit:
if ( NULL != sc_handle )
{
CloseServiceHandle( sc_handle );
sc_handle = ( SC_HANDLE )NULL;
}
if ( NULL != scm )
{
CloseServiceHandle( scm );
scm = ( SC_HANDLE )NULL;
}
return( ret );
} /* end of main */
/************************************************************************/
☆ Service相关操作
-
如果是远程Service操作,首先建立SMB会话:
net use \192.168.7.152\IPC$ /user:
如果不是远程Service操作,无须建立SMB会话。
-
远程安装服务,假设c:\helloservice.exe已经到位:
installservice.exe -t 192.168.7.152 -s HelloWorld -d "Hello World!" -c "c:\helloservice.exe"
-
远程启动服务
startservice -t 192.168.7.152 -s HelloWorld -f 500 -d 500 -i 1000 sc \192.168.7.152 start HelloWorld -f 500 -d 500 -i 1000
本地启动服务
net start HelloWorld
还可以在services.msc中指定ServiceMain()的参数
-
远程暂停服务
sc \192.168.7.152 pause HelloWorld
本地暂停服务
net pause HelloWorld
-
发送自定义控制命令
beepservice.exe -t 192.168.7.152 -s HelloWorld
-
远程恢复服务
sc \192.168.7.152 continue HelloWorld
本地恢复服务
net continue HelloWorld
-
远程停止服务
sc \192.168.7.152 stop HelloWorld
-
远程卸载服务
uninstallservice.exe -t 192.168.7.152 -s HelloWorld
注意,下面这条命令事先并不停止运行中的服务,只是直接删除注册表内容,而 服务继续保持运行状态:
sc \192.168.7.152 delete HelloWorld
☆ Service相关的注册表内容
installservice.exe -t 192.168.7.152 -s HelloWorld -d "Hello World!" -c "c:\helloservice.exe"
这条命令产生如下注册表内容:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HelloWorld]
"Type"=dword:00000010
"Start"=dword:00000003
"ErrorControl"=dword:00000001
"ImagePath"=hex(2):63,00,3a,00,5c,00,68,00,65,00,6c,00,6c,00,6f,00,73,00,65,00,
72,00,76,00,69,00,63,00,65,00,2e,00,65,00,78,00,65,00,00,00
"DisplayName"="Hello World!"
"ObjectName"="LocalSystem"
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HelloWorld\Security]
"Security"=hex:01,00,14,80,90,00,00,00,9c,00,00,00,14,00,00,00,30,00,00,00,02,
00,1c,00,01,00,00,00,02,80,14,00,ff,01,0f,00,01,01,00,00,00,00,00,01,00,00,
00,00,02,00,60,00,04,00,00,00,00,00,14,00,fd,01,02,00,01,01,00,00,00,00,00,
05,12,00,00,00,00,00,18,00,ff,01,0f,00,01,02,00,00,00,00,00,05,20,00,00,00,
20,02,00,00,00,00,14,00,8d,01,02,00,01,01,00,00,00,00,00,05,0b,00,00,00,00,
00,18,00,fd,01,02,00,01,02,00,00,00,00,00,05,20,00,00,00,23,02,00,00,01,01,
00,00,00,00,00,05,12,00,00,00,01,01,00,00,00,00,00,05,12,00,00,00
接着执行"net start HelloWorld",将产生如下注册表内容:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HelloWorld\Enum] "0"="Root\LEGACY_HELLOWORLD\0000" "Count"=dword:00000001 "NextInstance"=dword:00000001
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\Root\LEGACY_HELLOWORLD] "NextInstance"=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\Root\LEGACY_HELLOWORLD\0000] "Service"="HelloWorld" "Legacy"=dword:00000001 "ConfigFlags"=dword:00000000 "Class"="LegacyDriver" "ClassGUID"="{8ECC055D-047F-11D1-A537-0000F8753ED1}" "DeviceDesc"="Hello World!"
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\Root\LEGACY_HELLOWORLD\0000\Control] "NewlyCreated"=dword:00000000 "ActiveService"="HelloWorld"
执行"net stop HelloWorld",不会导致如上注册表内容消失。另有一处与上述内容 对应的注册表内容,但这不是新出现的,而是一直存在的:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class{8ECC055D-047F-11D1-A537-0000F8753ED1}] "Class"="LegacyDriver" @="Non-Plug and Play Drivers" "NoDisplayClass"="1" "SilentInstall"="1" "NoInstallClass"="1" "EnumPropPages32"="SysSetup.Dll,LegacyDriverPropPageProvider" "Icon"="-19"
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class{8ECC055D-047F-11D1-A537-0000F8753ED1}\0000]
对比<<MSDN系列(2)--学习写一个Hello World驱动>>中关于注册表的描述,看看有什 么异同。
uninstallservice.exe -t 192.168.7.152 -s HelloWorld
这条命令会删除installservice.exe以及net start命令增加上来的注册表内容。现 在明白CreateService()、DeleteService()在干什么了吧。
☆ 初次接触Service时的一点感性认识
sc远比net命令强大,比如可以指定ServiceMain()的参数。
Service编程比普通应用程序编程复杂,但也是死框架,有了一个模板,以后会轻松 下来,最终应该跟普通应用程序编程差不多难度。当然,如果考虑安全性的话,还是 要复杂些。
通过SMB会话可以远程添加服务、删除服务、启动服务等等。
尽管main()中的argv[1]不会传递给ServiceMain(),但在main()中仍可使用。
如果一个恶意程序在30秒内完成了必要操作,即使SCM启动服务失败也于事无补。
自安装服务程序应该就是将intallservice.c、startservice.c与helloservice.c结 合了一下,可能需要先复制自身(helloservice.exe)到某路径下,然后根据main()的 参数走不同的流程。另有一些程序,既可当作普通应用程序,也可当作服务,我看也 不是难事,最后干活的是HelloWorld_Thread(),只要把这个线程函数单独抽出来处 理好就成。这是我的个人想像,因为主旨无关,故未实践上述两类情形,记录在此备 忘,不可当真。
此外,MSDN让人又爱又恨,属于手册,但绝非入门的好教材。
☆ 参考资源
[ 1] <> - Marshall Brian[1996]
[ 2] An Introduction to NT Services http://www.commsoft.com/services.html