标题: MSDN系列(8)--学习写一个Hello World服务(Service)
创建: 2003-12-06 15:51 更新: 2003-12-09 16:32 链接: https://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
include
include
include
/********** * * * 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
include
include
include
/********** * * * 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
include
include
include
/********** * * * 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
include
include
include
/********** * * * 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
include
include
include
/********** * * * 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相关操作
1) 如果是远程Service操作,首先建立SMB会话:
net use \192.168.7.152\IPC$
如果不是远程Service操作,无须建立SMB会话。
2) 远程安装服务,假设c:\helloservice.exe已经到位:
installservice.exe -t 192.168.7.152 -s HelloWorld -d "Hello World!" -c "c:\helloservice.exe"
3) 远程启动服务
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()的参数
4) 远程暂停服务
sc \192.168.7.152 pause HelloWorld
本地暂停服务
net pause HelloWorld
5) 发送自定义控制命令
beepservice.exe -t 192.168.7.152 -s HelloWorld
6) 远程恢复服务
sc \192.168.7.152 continue HelloWorld
本地恢复服务
net continue HelloWorld
7) 远程停止服务
sc \192.168.7.152 stop HelloWorld
8) 远程卸载服务
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",将产生如下注册表内容:
1)
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
2)
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]
对比<
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] <
[ 2] An Introduction to NT Services http://www.commsoft.com/services.html