标题: 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$ /user: 如果不是远程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] <> - Marshall Brian[1996] [ 2] An Introduction to NT Services http://www.commsoft.com/services.html