| ||
Well, now the most interesting part of the chapter beginsnaturally, these are the examples. For clarity, I have divided the task into four subtasks : writing the service program, registering the service in the system registry, starting the service, and stopping the service and removing it from the registry. The SERV.EXE program presented in Listing 21.1 cannot be started in a normal way. If you try, you'll fail. The structure of this program requires it to be previously registered in the system registry using the SETSERV.EXE program shown in Listing 21.2. After that, the service can be started for execution using the STSERV.EXE program presented in Listing 21.3. The same result can be achieved using the standard Services console in the standard Administrative Tools window. Finally, the service can be deleted using the DELSERV.EXE program (Listing 21.4) whether it is running or not.
.586P ; Flat' memory model .MODEL FLAT, stdcall ; Constants SERVICE_CONTROL_STOP equ 1h SERVICE_CONTROL_SHUTDOWN equ 5h SERVICE_CONTROL_INTERROGATE equ 4h SERVICE_CONTROL_CONTINUE equ 3h SERVICE_START_PENDING equ 2h ERROR_SERVICE_SPECIFIC_ERROR equ 1066 SERVICE_RUNNING equ 4h MB_SERVICE_NOTIFICATION equ 200000h CRST EQU SERVICE_CONTROL_STOP OR \ SERVICE_CONTROL_SHUTDOWN OR \ SERVICE_CONTROL_CONTINUE SERVICE_WIN32_OWN_PROCESS equ 00000010h ; Prototypes of external procedures IFDEF MASM EXTERN Sleep@4:NEAR EXTERN SetServiceStatus@8:NEAR EXTERN RegisterServiceCtrlHandlerA@8:NEAR EXTERN StartServiceCtrlDispatcherA@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN MessageBoxA@16:NEAR ELSE EXTERN Sleep:NEAR EXTERN SetServiceStatus:NEAR EXTERN RegisterServiceCtrlHandlerA:NEAR EXTERN StartServiceCtrlDispatcherA:NEAR EXTERN ExitProcess:NEAR EXTERN MessageBoxA:NEAR Sleep@4 = Sleep SetServiceStatus@8 = SetServiceStatus MessageBoxA@16 = MessageBoxA RegisterServiceCtrlHandlerA@8 = RegisterServiceCtrlHandlerA StartServiceCtrlDispatcherA@4=StartServiceCtrlDispatcherA ExitProcess@4 = ExitProcess ENDIF ; INCLUDELIB directives for the linker IFDEF MASM includelib d:\masm32\lib\user32.lib includelib d:\masm32\lib\kernel32.lib includelib d:\masm32\lib\advapi32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ SSTATUS STRUC STYPE DD ? SSTATE DD ? SACCEPT DD ? SEXCODE DD ? SEXSCOD DD ? SCHEKPO DD ? SWAITHI DD ? SSTATUS ENDS ; Data segment _DATA SEGMENT SNAME DB "MyService", 0 DTS DD OFFSET SNAME, OFFSET WINSERV, 0, 0 SRS SSTATUS <0> H1 DD ? _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Service registration PUSH OFFSET DTS CALL StartServiceCtrlDispatcherA@4 ; Exit after the service has stopped PUSH 0 CALL ExitProcess@4 ; The service WINSERV PROC ; Fill the structure MOV SRS.STYPE, SERVICE_WIN32_OWN_PROCESS MOV SRS.SSTATE, SERVICE_RUNNING ; SERVICE_START_PENDING MOV SRS.SACCEPT, CRST MOV SRS.SEXCODE, 0 ; ERROR_SERVICE_SPECIFIC_ERROR MOV SRS.SEXSCOD, 0 MOV SRS.SCHEKPO, 0 MOV SRS.SWAITHI, 1 ; Register the command-handling function PUSH OFFSET HANDLER PUSH OFFSET SNAME CALL RegisterServiceCtrlHandlerA@8 ; Set the status MOV H1, EAX PUSH OFFSET SRS PUSH H1 CALL SetServiceStatus@8 ; Main function of this service starts here PUSH 200000 CALL Sleep@4 ; Set the status MOV SRS.SSTATE, SERVICE_CONTROL_STOP PUSH OFFSET SRS PUSH H1 CALL SetServiceStatus@8 RET 8 WINSERV ENDP ; Interrupt handler ; [EBP+08H] - The only parameter HANDLER PROC PUSH EBP MOV EBP, ESP INC SRS.SCHEKPO MOV EAX, DWORD PTR [EBP+08H] CMP EAX, SERVICE_CONTROL_STOP JNZ NO_STOP MOV SRS.SSTATE, SERVICE_CONTROL_STOP JMP _SET NO_STOP: CMP EAX, SERVICE_CONTROL_SHUTDOWN JNZ NO_SHUTDOWN MOV SRS.SSTATE, SERVICE_CONTROL_STOP JMP _SET NO_SHUTDOWN: CMP EAX, SERVICE_CONTROL_CONTINUE JNZ NO_CONTINUE MOV SRS.SSTATE, SERVICE_CONTROL_CONTINUE JMP _SET NO_CONTINUE: CMP EAX, SERVICE_CONTROL_INTERROGATE ; Set the service status _SET: PUSH OFFSET SRS PUSH H1 CALL SetServiceStatus@8 ; The message included for debugging ; The MB_SERVICE_NOTIFICATION constant is required PUSH 0 OR MB_SERVICE_NOTIFICATION PUSH OFFSET SNAME PUSH OFFSET SNAME PUSH 0 CALL MessageBoxA@16 ;---------------------------- MOV ESP, EBP POP EBP RET 4 HANDLER ENDP _TEXT ENDS END START
The program presented in Listing 21.1 requires some comments.
Pay attention to the program structure. As already mentioned, it contains the main function (the START label), the logical service (in this program, there is only one such service WINSERV ), and the command handler called HANDLER .
For simplicity, I used the Sleep function with a long delay as the executable process. When the STSERV program starts for execution, this function is started. Principally, this service doesn't need to be stopped forcibly , because after the termination of the sleep function it will stop automatically.
The WINSERV function is terminated by RET 8 . This means that two parameters are sent to the function but are never processed (see the description of StartService ).
From the Assembly language programmer's point of view, the structure of the HANDLER procedure is simple. Principally, this handler is needed only for processing the SERVICE_CONTROL_STOP command.
For debugging purposes, I have placed the MessageBox function into the command handler. This aspect is quite interesting. The message must appear in relation to a specific desktop. Because of this, I used the MB_SERVICE_NOTIFICATION constant intended for displaying the messages from the service. This message will appear even if no user is logged on to the computer.
The RET 4 command that terminates the handler means that only one parameterthe command to the serviceis accepted in the course of the call.
To translate this program, issue the following commands for MASM32:
ML /c /coff /DMASM serv.asm LINK /SUBSYSTEM.CONSOLE serv.obj
Issue the following commands for TASM32:
TASM32 /ml serv.asm TLINK32 -ap serv.obj
.586P ; Flat memory model .MODEL FLAT, stdcall ; Constants STD_OUTPUT_HANDLE equ -11 SC_MANAGER_ALL_ACCESS equ 0F003Fh SERVICE_ALL_ACCESS equ 0F01FFH SERVICE_WIN32_OWN_PROCESS equ 00000010h SERVICE_DEMAND_START equ 00000003h SERVICE_ERROR_NORMAL equ 00000001h ; Prototypes of external procedures IFDEF MASM EXTERN CreateServiceA@52:NEAR EXTERN CloseServiceHandle@4:NEAR EXTERN OpenSCManagerA@12:NEAR EXTERN wsprint fA:NEAR EXTERN GetLastError@0:NEAR EXTERN StartServiceCtrlDispatcherA@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN lstrlenA@4:NEAR EXTERN WriteConsoleA@20:NEAR EXTERN GetStdHandle@4:NEAR ELSE EXTERN CreateServiceA:NEAR EXTERN CloseServiceHandle:NEAR EXTERN OpenSCManagerA:NEAR EXTERN _wsprintfA:NEAR EXTERN GetLastError:NEAR EXTERN StartServiceCtrlDispatcherA:NEAR EXTERN ExitProcess:NEAR EXTERN MessageBoxA:NEAR EXTERN lstrlenA:NEAR EXTERN WriteConsoleA:NEAR EXTERN GetStdHandle:NEAR CreateServiceA@52 = CreateServiceA CloseServiceHandle@4 = CloseServiceHandle OpenSCManagerA@12 = OpenSCManagerA GetStdHandle@4 = GetStdHandle WriteConsoleA@20 = WriteConsoleA lstrlenA@4 = lstrlenA wsprintfA = _wsprintfA GetLastError@0 = GetLastError StartServiceCtrlDispatcherA@4 = StartServiceCtrlDispatcherA ExitProcess@4 = ExitProcess ENDIF ; INCLUDELIB directives for the linker IFDEF MASM includelib d:\masm32\lib\user32.lib includelib d:\masm32\lib\kernel32.lib includelib d:\masm32\lib\advapi32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;----------------------------------------------- ; Data segment _DATA SEGMENT H1 DD ? H2 DD ? ALIGN 4 SNAME1 DB "MyService", 0 ALIGN 4 NM DB "D:\masm32\BIN\serv.exe", 0 LENS DD 0 HANDL DD 0 BUF1 DB 512 DUP(0) ERRS DB "Error %u ", 0 _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Define the output console handle PUSH STD_OUTPUT_HANDLE CALL GetStdHandle@4 MOV HANDL, EAX ; Open the services database PUSH SC_MANAGER_ALL_ACCESS PUSH 0 PUSH 0 CALL OpenSCManagerA@12 CMP EAX, 0 JNZ NO_ERR CALL ERROB JMP EXI NO_ERR: MOV H1, EAX ; Identifier received; now, create the service PUSH 0 PUSH 0 PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET NM PUSH SERVICE_ERROR_NORMAL PUSH SERVICE_DEMAND_START PUSH SERVICE_WIN32_OWN_PROCESS PUSH SERVICE_ALL_ACCESS PUSH OFFSET SNAME1 PUSH OFFSET SNAME1 PUSH H1 CALL CreateServiceA@52 CMP EAX, 0 JNZ CLOS MOV H2, EAX CALL ERROB JMP CLOS1 ; Error-handling block ERROB: CALL GetLastError@0 PUSH EAX PUSH OFFSET ERRS PUSH OFFSET BUF1 CALL wsprintfA ADD ESP, 12 LEA EAX, BUF1 MOV EDI, 1 CALL WRITE RET CLOSE1: ; Close the service PUSH H2 CALL CloseServiceHandle@4 CLOSE: ; Close the services database PUSH H1 CALL CloseServiceHandle@4 EXI: ; Exit after all services terminate PUSH 0 CALL ExitProcess@4 ; Output the string (line feed in the end) ; EAX --- To the start of the string ; EDI - With or without the line feed WRITE PROC ; Get the parameter length PUSH EAX PUSH EAX CALL lstrlenA@4 MOV ESI, EAX POP EBX CMP EDI, 1 JNE NO_ENT ; Line feed in the end MOV BYTE PTR [EBX+ESI], 13 MOV BYTE PTR [EBX+ESI+1], 10 MOV BYTE PTR [EBX+ESI+2], 0 ADD EAX, 2 NO_ENT: ; String output PUSH 0 PUSH OFFSET LENS PUSH EAX PUSH EBX PUSH HANDL CALL WriteConsoleA@20 RET WRITE ENDP _TEXT ENDS END START
When considering the program presented in Listing 21.2 (SETSERV.EXE), pay attention to the following aspects:
This program registers the SERV.EXE service in the system registry using the CreateService API function.
Note that here you handle all possible errors when using this function and, if an error has occurred, you output the error code to the console.
To translate this program, issue the following commands for MASM32:
ML /c /coff /DMASM setserv.asm LINK /SUBSYSTEM:CONSOLE setserv.obj
Issue the following commands for TASM32:
TASM32 /ml setserv.asm TLINK32 -ap setserv.obj
.586P ; Flat memory model .MODEL FLAT, stdcall ; Constants DELETE equ 10000h STD_OUTPUT_HANDLE equ -11 SC_MANAGER_ALL_ACCESS equ 0F003Fh SERVICE_ALL_ACCESS equ 0F01FFH SERVICE_WIN32_OWN_PROCESS equ 00000010h SERVICE_DEMAND_START equ 00000003h SERVICE_ERROR_NORMAL equ 00000001h SERVICE_CONTROL_STOP equ 1h ; Prototypes of external procedures IFDEF MASM EXTERN StartServiceA@12:NEAR EXTERN OpenServiceA@12:NEAR EXTERN CloseServiceHandle@4:NEAR EXTERN OpenSCManagerA@12:NEAR EXTERN wsprintfA:NEAR EXTERN GetLastError@0:NEAR EXTERN ExitProcess@4:NEAR EXTERN lstrlenA@4:NEAR EXTERN WriteConsoleA@20:NEAR EXTERN GetStdHandle@4:NEAR ELSE EXTERN StartServiceA:NEAR EXTERN OpenServiceA:NEAR EXTERN CloseServiceHandle:NEAR EXTERN OpenSCManagerA:NEAR EXTERN _wsprintfA:NEAR EXTERN GetLastError:NEAR EXTERN ExitProcess:NEAR EXTERN lstrlenA:NEAR EXTERN WriteConsoleA:NEAR EXTERN GetStdHandle:NEAR StartServiceA@12 = StartServiceA OpenServiceA@12 = OpenServiceA CloseServiceHandle@4 = CloseServiceHandle OpenSCManagerA@12 = OpenSCManagerA GetStdHandle@4 = GetStdHandle WriteConsoleA@20 = WriteConsoleA lstrlenA@4 = lstrlenA wsprintfA = _wsprintfA GetLastError@0 = GetLastError ExitProcess@4 = ExitProcess ENDIF ; INCLUDELIB directives for the linker IFDEF MASM includelib d:\masm32\lib\user32.lib includelib d:\masm32\lib\kernel32.lib includelib d:\masm32\lib\advapi32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;----------------------------------------------- SSTATUS STRUC STYPE DD ? SSTATE DD ? SACCEPT DD ? SEXCODE DD ? SEXSCOD DD ? SCHEKPO DD ? SWAITHI DD ? SSTATUS ENDS ; Data segment _DATA SEGMENT SRS SSTATUS <?> H1 DD ? H2 DD ? ALIGN 4 SNAME1 DB "MyService", 0 ALIGN 4 LENS DD 0 HANDL DD 0 BUF1 DB 512 DUP(0) ERRS DB "Error %u ", 0 _DATA ENDS ; Code segment _TEXT SEGMENT START: PUSH STD OUTPUT_HANDLE CALL GetStdHandle@4 MOV HANDL, EAX ;------------------------------ PUSH SC_MANAGER_ALL_ACCESS PUSH 0 PUSH 0 CALL OpenSCManagerA@12 CMP EAX, 0 JNZ NO_ERR1 CALL ERROB JMP EXI NO_ERR1: MOV H1, EAX ; Identifier received PUSH SC_MANAGER_ALL_ACCESS ; DELETE PUSH OFFSET SNAME1 PUSH H1 CALL OpenServiceA@12 CMP EAX, 0 JNZ NO_ERR2 CALL ERROB JMP CLOSE NO_ERR2. MOV H2, EAX ; Service identifier received ; Send the command for starting the service PUSH 0 PUSH 0 PUSH H2 CALL StartServiceA@12 CMP EAX, 0 JNZ CLOSE1 CALL ERROB JMP CLOS1 ; Error-handling block ERROB: CALL GetLastError@0 PUSH EAX PUSH OFFSET ERRS PUSH OFFSET BUF1 CALL wsprintfA ADD ESP, 12 LEA EAX, BUF1 MOV EDI, 1 CALL WRITE RET CLOSE1: ; Close the service PUSH H2 CALL CloseServiceHandle@4 ; Close the services database CLOSE: PUSH H1 CALL CloseServiceHandle@4 EXI: ; Exit PUSH 0 CALL ExitProcess@4 ; Output the string (line feed in the end) ; EAX --- To the start of the string ; EDI --- With or without line feed WRITE PROC ; Get the parameter length PUSH EAX PUSH EAX CALL lstrlenA@4 MOV ESI, EAX POP EBX CMP EDI, 1 JNE NO_ENT ; Line feed in the end MOV BYTE PTR [EBX+ESI], 13 MOV BYTE PTR [EBX+ESI+1], 10 MOV BYTE PTR [EBX+ESI+2], 0 ADD EAX, 2 NO_ENT: ; String output PUSH 0 PUSH OFFSET LENS PUSH EAX PUSH EBX PUSH HANDL CALL WriteConsoleA@20 RET WRITE ENDP _TEXT ENDS END START
The STSERV.EXE program presented in Listing 21.3 starts the service with the name MyService for execution. The start is carried out using the StartService API function.
To translate this program, issue the following commands for MASM32:
ML /c /coff /DMASM stserv.asm LINK /SUBSYSTEM:CONSOLE stserv.obj
Issue the following commands for TASM32:
TASM32 /ml stserv.asm TLINK32 -ap stserv.obj
.586P ; Flat memory model .MODEL FLAT, stdcall ; Constants DELETE equ 10000h STD_OUTPUT_HANDLE equ -11 SC_MANAGER_ALL_ACCESS equ OF003Fh SERVICE_ALL_ACCESS equ 0F01FFH SERVICE_WIN32_OWN_PROCESS equ 00000010h SERVICE_DEMAND_START equ 00000003h SERVICE_ERROR_NORMAL equ 00000001h SERVICE_CONTROL_STOP equ 1h ; Prototypes of external procedures IFDEF MASM EXTERN ControlService@12:NEAR EXTERN DeleteService@4:NEAR EXTERN OpenServiceA@12:NEAR EXTERN CloseServiceHandle@4:NEAR EXTERN OpenSCManagerA@12:NEAR EXTERN wsprintfA:NEAR EXTERN GetLastError@0:NEAR EXTERN ExitProcess@4:NEAR EXTERN lstrlenA@ 4:NEAR EXTERN WriteConsoleA@20:NEAR EXTERN GetStdHandle@4:NEAR ELSE EXTERN ControlService:NEAR EXTERN DeleteService:NEAR EXTERN OpenServiceA:NEAR EXTERN CloseServiceHandle:NEAR EXTERN OpenSCManagerA:NEAR EXTERN _wsprintfA:NEAR EXTERN GetLastError:NEAR EXTERN ExitProcess:NEAR EXTERN lstrlenA:NEAR EXTERN WriteConsoleA:NEAR EXTERN GetStdHandle:NEAR ControlService@12 = ControlService DeleteService@4 = DeleteService OpenServiceA@12 = OpenServiceA CloseServiceHandle@4 = CloseServiceHandle OpenSCManagerA@12 = OpenSCManagerA GetStdHandle@4 = GetStdHandle WriteConsoleA@20 = WriteConsoleA lstrlenA@4 = lstrlenA wsprintfA = _wsprintfA GetLastError@0 = GetLastError StartServiceCtrlDispatcherA@4 = StartServiceCtrlDispatcherA MessageBoxA@16 = MessageBoxA ExitProcess@4 = ExitProcess ENDIF ; INCLUDELIB directives for the linker IFDEF MASM includelib d:\masm32\lib\user32.lib includelib d:\masm32\lib\kernel32.lib includelib d:\masm32\lib\advapi32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;---------------------------------------------- SSTATUS STRUC STYPE DD ? SSTATE DD ? SACCEPT DD ? SEXCODE DD ? SEXSCOD DD ? SCHEKPO DD ? SWAITHI DD ? SSTATUS ENDS ; Data segment _DATA SEGMENT SRS SSTATUS <?> H1 DD ? H2 DD ? ALIGN 4 SNAME1 DB "MyService", 0 ALIGN 4 LENS DD 0 HANDL DD 0 BUF1 DB 512 DUP(0) ERRS DB "Error %u ", 0 _DATA ENDS ; Code segment _TEXT SEGMENT START: PUSH STD_OUTPUT_HANDLE CALL GetStdHandle@4 MOV HANDL, EAX ; Open the services database PUSH SC_MANAGER_ALL_ACCESS PUSH 0 PUSH 0 CALL OpenSCManagerA@12 CMP EAX, 0 JNZ Z1 CALL ERROB JMP EXI Z1: MOV H1, EAX ; Identifier has been received; open the service PUSH SC_MANAGER_ALL_ACCESS ; DELETE PUSH OFFSET SNAME1 PUSH H1 CALL OpenServiceA@12 CMP EAX, 0 JNZ Z2 CALL ERROB JMP CLOSE Z2: MOV H2, EAX ; Send the stop command PUSH OFFSET SRS PUSH SERVICE_CONTROL_STOP PUSH H2 CALL ControlService@12 CMP EAX, 0 JNZ Z3 CALL ERROB Z3: ; Delete the service PUSH H2 CALL DeleteService@4 CMP EAX, 0 JNZ CLOSE CALL ERROB JMP CLOSE1 ; Error-handling block ERROB: CALL GetLastError@0 PUSH EAX ' PUSH OFFSET ERRS PUSH OFFSET BUF1 CALL wsprintfA ADD ESP, 12 LEA EAX, BUF1 MOV EDI, 1 CALL WRITE RET CLOSE1: ; Close the service PUSH H2 CALL CloseServiceHandle@4 CLOSE: ; Close the services database PUSH H1 CALL CloseServiceHandle@4 EXI: ; Exit when all services stop PUSH 0 CALL ExitProcess@4 ; Output the string (line feed in the end) ; EAX --- To the start of the string ; EDI --- With or without the line feed WRITE PROC ; Get the parameter length PUSH EAX PUSH EAX CALL lstrlenA@4 MOV ESI, EAX POP EBX CMP EDI, 1 JNE NO_ENT ; Line feed in the end MOV BYTE PTR [EBX+ESI], 13 MOV BYTE PTR [EBX+ESI+1], 10 MOV BYTE PTR [EBX+ESI+2], 0 ADD EAX, 2 NO_ENT: ; String output PUSH 0 PUSH OFFSET LENS PUSH EAX PUSH EBX PUSH HANDL CALL WriteConsoleA@20 RET WRITE ENDP _TEXT ENDS END START
To translate this program, issue the following commands for MASM32:
ML /c /coff /DMASM delserv.asm LINK /SUBSYSTEM:CONSOLE delserv.obj
Issue the following commands for TASM32:
TASM32 /ml delserv.asm TLINK32 -ap delserv.obj
This program requires some comments.
This example shows how it is possible to delete the service even if it is running. This can be achieved if the service makes provision for a reaction to the stop command. Principally, everything is self-evident. However, note that even if an error occurs when executing the ControlService API function, everything will continue to execute in due order. The point is that this command sends the stop command to the service. However, if the service is registered in the services database but is not running, then the function will return error code 1062, which means that the service has been stopped already.
| ||