From 99d39604df0b34690d8cbb7825eb5afdf14f6a7e Mon Sep 17 00:00:00 2001 From: hakril Date: Tue, 31 Dec 2024 10:23:08 +0100 Subject: [PATCH] Finishing samples/python_service.py --- .../definitions/defines/services.txt | 26 +++- docs/source/winfuncs_generated.rst | 12 ++ docs/source/winstructs_generated.rst | 4 + samples/service/python_service.py | 124 ++++++++++++++++++ windows/winobject/service.py | 7 +- 5 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 samples/service/python_service.py diff --git a/ctypes_generation/definitions/defines/services.txt b/ctypes_generation/definitions/defines/services.txt index 40bd924..c5788c0 100644 --- a/ctypes_generation/definitions/defines/services.txt +++ b/ctypes_generation/definitions/defines/services.txt @@ -116,4 +116,28 @@ SERVICE_WIN32_OWN_PROCESS) #define SERVICE_ACCEPT_SESSIONCHANGE 0x00000080 #define SERVICE_ACCEPT_PRESHUTDOWN 0x00000100 #define SERVICE_ACCEPT_TIMECHANGE 0x00000200 -#define SERVICE_ACCEPT_TRIGGEREVENT 0x00000400 \ No newline at end of file +#define SERVICE_ACCEPT_TRIGGEREVENT 0x00000400 + +// +// Service object specific access type +// +#define SERVICE_QUERY_CONFIG 0x0001 +#define SERVICE_CHANGE_CONFIG 0x0002 +#define SERVICE_QUERY_STATUS 0x0004 +#define SERVICE_ENUMERATE_DEPENDENTS 0x0008 +#define SERVICE_START 0x0010 +#define SERVICE_STOP 0x0020 +#define SERVICE_PAUSE_CONTINUE 0x0040 +#define SERVICE_INTERROGATE 0x0080 +#define SERVICE_USER_DEFINED_CONTROL 0x0100 + +#define SERVICE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \ + SERVICE_QUERY_CONFIG | \ + SERVICE_CHANGE_CONFIG | \ + SERVICE_QUERY_STATUS | \ + SERVICE_ENUMERATE_DEPENDENTS | \ + SERVICE_START | \ + SERVICE_STOP | \ + SERVICE_PAUSE_CONTINUE | \ + SERVICE_INTERROGATE | \ + SERVICE_USER_DEFINED_CONTROL) \ No newline at end of file diff --git a/docs/source/winfuncs_generated.rst b/docs/source/winfuncs_generated.rst index c137398..80cb453 100644 --- a/docs/source/winfuncs_generated.rst +++ b/docs/source/winfuncs_generated.rst @@ -922,6 +922,18 @@ Functions .. function:: StartServiceCtrlDispatcherW(lpServiceStartTable) +.. function:: RegisterServiceCtrlHandlerExA(lpServiceName, lpHandlerProc, lpContext) + +.. function:: RegisterServiceCtrlHandlerExW(lpServiceName, lpHandlerProc, lpContext) + +.. function:: RegisterServiceCtrlHandlerA(lpServiceName, lpHandlerProc) + +.. function:: RegisterServiceCtrlHandlerW(lpServiceName, lpHandlerProc) + +.. function:: SetServiceStatus(hServiceStatus, lpServiceStatus) + +.. function:: SetServiceBits(hServiceStatus, dwServiceBits, bSetBitsOn, bUpdateImmediately) + .. function:: SetupDiClassNameFromGuidA(ClassGuid, ClassName, ClassNameSize, RequiredSize) .. function:: SetupDiClassNameFromGuidW(ClassGuid, ClassName, ClassNameSize, RequiredSize) diff --git a/docs/source/winstructs_generated.rst b/docs/source/winstructs_generated.rst index 4648862..3892063 100644 --- a/docs/source/winstructs_generated.rst +++ b/docs/source/winstructs_generated.rst @@ -511,6 +511,10 @@ Simple types .. autoclass:: PDNS_QUERY_COMPLETION_ROUTINE +.. autoclass:: LPHANDLER_FUNCTION + +.. autoclass:: LPHANDLER_FUNCTION_EX + .. autoclass:: LPCONTEXT .. autoclass:: HCERTSTORE diff --git a/samples/service/python_service.py b/samples/service/python_service.py new file mode 100644 index 0000000..9972e76 --- /dev/null +++ b/samples/service/python_service.py @@ -0,0 +1,124 @@ +import sys +import os.path +import argparse +import datetime +import ctypes + +import windows +import windows.generated_def as gdef + +SERVICE_NAME = u"PFW_SERVICE_DEMO" +SERVICE_DESCRIPTION = u"PythonForWindows demo service" + +SERVICE_LOGFILE = os.path.join(os.path.dirname(__file__), "logs.txt") + +SERVICE_HANDLE = None + +def install_demo_service(): + path = "{executable} {pyfile} --run".format(executable=sys.executable, pyfile=__file__) + print("Registering service <{0}> as : <{1}>".format(SERVICE_NAME, path)) + newservice = windows.system.services.create( + name=SERVICE_NAME, + description=SERVICE_DESCRIPTION, + access=gdef.SERVICE_ALL_ACCESS, + type=gdef.SERVICE_WIN32_OWN_PROCESS, + start=gdef.SERVICE_DEMAND_START, + path=path, + user=None + ) + print(newservice) + return + +def uninstall_demo_service(): + print("Deleting service") + print(windows.system.services[SERVICE_NAME].delete()) + + + +def log(s): + with open(SERVICE_LOGFILE, "a") as f: + f.write("[{time}] {s}\n".format(time=datetime.datetime.now(), s=s)) + + +@ctypes.WINFUNCTYPE(gdef.DWORD, gdef.DWORD, gdef.DWORD, gdef.PVOID, gdef.PVOID) +def service_handlerex(dwControl, dwEventType, lpEventData, lpContext): + log("in service_handlerex") + log("service_handlerex: called with {0}".format(dwControl)) + if dwControl == gdef.SERVICE_CONTROL_STOP: + log("Stopping the service") + running_status = gdef.SERVICE_STATUS( + dwServiceSpecificExitCode=0, + dwServiceType =gdef.SERVICE_WIN32_OWN_PROCESS, + dwCurrentState=gdef.SERVICE_STOPPED, + dwWin32ExitCode=gdef.NO_ERROR, + dwControlsAccepted=0 + ) + try: + windows.winproxy.SetServiceStatus(SERVICE_HANDLE, running_status) + except Exception as e: + log(str(e)) + return 0 + +@ctypes.WINFUNCTYPE(gdef.PVOID, gdef.DWORD, ctypes.POINTER(gdef.LPWSTR)) +def service_main(dwNumServicesArgs, lpServiceArgVectors): + global SERVICE_HANDLE + log("In service_main") + log("service_main: {0}".format(dwNumServicesArgs)) + log("service_main: {0}".format(lpServiceArgVectors[0])) + + try: + log("Calling RegisterServiceCtrlHandlerExW") + SERVICE_HANDLE = windows.winproxy.RegisterServiceCtrlHandlerExW(SERVICE_NAME, ctypes.cast(service_handlerex, gdef.PVOID), None) + log("RegisterServiceCtrlHandlerExW handle: {0}".format(SERVICE_HANDLE)) + + running_status = gdef.SERVICE_STATUS( + dwServiceSpecificExitCode=0, + dwServiceType =gdef.SERVICE_WIN32_OWN_PROCESS, + dwCurrentState=gdef.SERVICE_RUNNING, + dwWin32ExitCode=gdef.NO_ERROR, + dwControlsAccepted=gdef.SERVICE_ACCEPT_STOP | gdef.SERVICE_ACCEPT_PAUSE_CONTINUE + ) + res = windows.winproxy.SetServiceStatus(SERVICE_HANDLE, running_status) + log("Service is running : {0}".format(res)) + except Exception as e: + log(str(e)) + raise + + return None + +def run_demo_service(): + log("start of run_demo_service()") + try: + SERVICE_TABLE = (gdef.SERVICE_TABLE_ENTRYW * 2)( + gdef.SERVICE_TABLE_ENTRYW(SERVICE_NAME, ctypes.cast(service_main, gdef.PVOID)), + gdef.SERVICE_TABLE_ENTRYW(None, None), + ) + log("Calling: StartServiceCtrlDispatcherW()") + result = windows.winproxy.StartServiceCtrlDispatcherW(SERVICE_TABLE) + log("StartServiceCtrlDispatcherW returned: {0}".format(result)) + log("Quitting") + except Exception as e: + log(str(e)) + raise + + + +parser = argparse.ArgumentParser(prog=__file__, formatter_class=argparse.ArgumentDefaultsHelpFormatter) +group = parser.add_mutually_exclusive_group(required=True) +group.add_argument("--install", action="store_true", help="Install the service in registry") +group.add_argument("--uninstall", action="store_true", help="UnInstall the service in registry") +group.add_argument("--run", action="store_true", help="Called by the services.exe to run the service") + + +if __name__ == "__main__": + args = parser.parse_args() + if args.install: + install_demo_service() + elif args.uninstall: + uninstall_demo_service() + elif args.run: + log("calling run_demo_service()") + log(sys.argv) + run_demo_service() + else: + raise ValueError("Unknown argument") \ No newline at end of file diff --git a/windows/winobject/service.py b/windows/winobject/service.py index 7f406d8..d2e7913 100644 --- a/windows/winobject/service.py +++ b/windows/winobject/service.py @@ -149,7 +149,7 @@ class ServiceManager(utils.AutoHandle): def enumerate_services(self): return list(self._enumerate_services_generator()) - def create(self, name, description, access, type, start, path): + def create(self, name, description, access, type, start, path, user=None): newservice_handle = windows.winproxy.CreateServiceW( self.handle, # hSCManager name, # lpServiceName @@ -162,7 +162,7 @@ class ServiceManager(utils.AutoHandle): None, # lpLoadOrderGroup None, # lpdwTagId None, # lpDependencies - None, # lpServiceStartName + user, # lpServiceStartName None) # lpPassword return Service(handle=newservice_handle, name=name, description=description) @@ -238,6 +238,9 @@ class Service(gdef.SC_HANDLE): windows.winproxy.ControlService(self, gdef.SERVICE_CONTROL_STOP, status) return status + def delete(self): + return windows.winproxy.DeleteService(self) + def __repr__(self): return urepr_encode(u"""<{0} "{1}" {2!r}>""".format(type(self).__name__, self.name, self.status.state))