Skip to content

Latest commit

 

History

History
1574 lines (1382 loc) · 50.7 KB

200312091659.txt.md

File metadata and controls

1574 lines (1382 loc) · 50.7 KB

标题: 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相关操作

  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",将产生如下注册表内容:


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