From 72bf8d917a2a50ab35f111b8e675bc9abe0478e5 Mon Sep 17 00:00:00 2001 From: nihonium Date: Fri, 13 Dec 2024 02:18:12 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=A0=D0=B0=D0=B1=D0=BE=D1=87=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F=20=D1=81=D0=B5?= =?UTF-8?q?=D1=80=D0=B2=D0=B8=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- client.c | 135 ++++++++++++++++++++++++++++++++++++++++++++ client.h | 16 ++++++ main.c | 72 +++++++++++++++++++++++- service.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++ service.h | 16 ++++++ 6 files changed, 388 insertions(+), 2 deletions(-) create mode 100644 client.c create mode 100644 client.h create mode 100644 service.c create mode 100644 service.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c0dc9d..6396638 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ endif() project ("shell-server") # Добавьте источник в исполняемый файл этого проекта. -add_executable (shell-server "main.c" "server.c" "server.h" "client.c" "client.h") +add_executable (shell-server "main.c" "server.c" "server.h" "client.c" "client.h" "service.c" "service.h") if (CMAKE_VERSION VERSION_GREATER 3.12) set_property(TARGET shell-server PROPERTY CXX_STANDARD 20) diff --git a/client.c b/client.c new file mode 100644 index 0000000..43882fd --- /dev/null +++ b/client.c @@ -0,0 +1,135 @@ +#include "client.h" +#include "server.h" + +#define DEFAULT_BUFLEN 512 + +SOCKET ConnectSocket = INVALID_SOCKET; + +DWORD WINAPI ClientWriteToPipe(LPDWORD dummy); +DWORD WINAPI ClientReadFromPipe(LPDWORD dummy); + +VOID StartShellClient(TCHAR *ServerIP) { + WSADATA wsaData; + int iResult; + struct addrinfo* result = NULL, *ptr = NULL, hints; + + // Initialize Winsock + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + printf("WSAStartup failed: %d\n", iResult); + return 1; + } + + // Create socket for client + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + // Resolve the server address and port + iResult = getaddrinfo(ServerIP, DEFAULT_PORT, &hints, &result); + if (iResult != 0) { + printf("getaddrinfo failed: %d\n", iResult); + WSACleanup(); + return 1; + } + + // Attempt to connect to the first address returned by the call to getaddrinfo + ptr = result; + + // Create a SOCKET for connecting to server + ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, + ptr->ai_protocol); + + if (ConnectSocket == INVALID_SOCKET) { + printf("Error at socket(): %ld\n", WSAGetLastError()); + freeaddrinfo(result); + WSACleanup(); + return 1; + } + + // Connect to a socket + // + // Connect to server. + iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); + if (iResult == SOCKET_ERROR) { + closesocket(ConnectSocket); + ConnectSocket = INVALID_SOCKET; + } + + freeaddrinfo(result); + + if (ConnectSocket == INVALID_SOCKET) { + printf("Unable to connect to server!\n"); + WSACleanup(); + return 1; + } + + HANDLE threads[2]; + threads[0] = CreateThread(NULL, 0, ClientWriteToPipe, NULL, 0, NULL); + threads[1] = CreateThread(NULL, 0, ClientReadFromPipe, NULL, 0, NULL); + + WaitForMultipleObjects(2, threads, TRUE, INFINITE); + + for (int i = 0; i < 2; ++i) + CloseHandle(threads[i]); + + // Finalize + // shutdown the send half of the connection since no more data will be sent + iResult = shutdown(ConnectSocket, SD_SEND); + if (iResult == SOCKET_ERROR) { + printf("shutdown failed: %d\n", WSAGetLastError()); + closesocket(ConnectSocket); + WSACleanup(); + return 1; + } + + // cleanup + closesocket(ConnectSocket); + WSACleanup(); + + return 0; +} + +DWORD WINAPI ClientWriteToPipe(LPDWORD dummy) { + UNREFERENCED_PARAMETER(dummy); + DWORD iResult, dwRead, bSuccess; + + for (;;) { + CHAR chBuf[DEFAULT_BUFLEN] = ""; + + bSuccess = + ReadFile(GetStdHandle(STD_INPUT_HANDLE), chBuf, DEFAULT_BUFLEN, &dwRead, NULL); + if (!bSuccess) { + return 1; + } + + iResult = send(ConnectSocket, chBuf, dwRead, 0); + if (iResult == SOCKET_ERROR) { + return -1; + } + } +} + +DWORD WINAPI ClientReadFromPipe(LPDWORD dummy) { + UNREFERENCED_PARAMETER(dummy); + DWORD iResult, dwRead, bSuccess; + + for (;;) { + CHAR chBuf[DEFAULT_BUFLEN + 1] = ""; + + iResult = recv(ConnectSocket, chBuf, DEFAULT_BUFLEN, 0); + if (iResult > 0) { + chBuf[strlen(chBuf)] = 0; + bSuccess = WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), chBuf, + strlen(chBuf), &dwRead, NULL); + if (!bSuccess) { + return 1; + } + } + else { + return -1; + } + } +} \ No newline at end of file diff --git a/client.h b/client.h new file mode 100644 index 0000000..d3b1107 --- /dev/null +++ b/client.h @@ -0,0 +1,16 @@ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#pragma comment(lib, "Ws2_32.lib") + +#include +#include +#include +#include + +#include +#include + +// Defined functions +VOID StartShellClient(TCHAR *ServerIP); \ No newline at end of file diff --git a/main.c b/main.c index e7477bb..866a99a 100644 --- a/main.c +++ b/main.c @@ -2,13 +2,31 @@ #include #include "server.h" +#include "service.h" +#include "client.h" + +SERVICE_STATUS gSvcStatus; +SERVICE_STATUS_HANDLE gSvcStatusHandle; +HANDLE ghSvcStopEvent = NULL; +HANDLE ghExecuteService = NULL; + +int _tmain(int argc, TCHAR *argv[]) { + SERVICE_TABLE_ENTRY DispatchTable[] = { + {SVCNAME, (LPSERVICE_MAIN_FUNCTION)SvcMain}, + {NULL, NULL} }; -int _tmain(int argc, char *argv[]) { if (argc == 2) { if (lstrcmpi(argv[1], TEXT("-s")) == 0) { StartShellServer(); return 0; } + else if (lstrcmpi(argv[1], TEXT("-sc")) == 0) { + if (!StartServiceCtrlDispatcher(DispatchTable)) { + printf("Error while starting Dispatcher Table : %d\n", GetLastError()); + ERROR_FAILED_SERVICE_CONTROLLER_CONNECT; + SvcReportEvent(TEXT("StartServiceCtrlDispatcher")); + } + } else { goto help_message; } @@ -22,6 +40,7 @@ int _tmain(int argc, char *argv[]) { // TODO: implement service else if (lstrcmpi(argv[1], TEXT("-s")) == 0 && lstrcmpi(argv[2], TEXT("-service")) == 0) { // CreateService + SvcInstall(); return 0; } else { @@ -38,3 +57,54 @@ help_message: return -1; } +VOID WINAPI SvcMain(DWORD dwArgc, LPTSTR* lpszArgv) { + // Register the handler function for the service + gSvcStatusHandle = RegisterServiceCtrlHandler(SVCNAME, SvcCtrlHandler); + + if (!gSvcStatusHandle) { + SvcReportEvent(TEXT("RegisterServiceCtrlHandler")); + return; + } + + // These SERVICE_STATUS members remain as set here + gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + gSvcStatus.dwServiceSpecificExitCode = 0; + + // Report initial status to the SCM + ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); + // Perform service-specific initialization and work. + SvcInit(dwArgc, lpszArgv); +} + +DWORD WINAPI ServerThreadStart(LPDWORD dummy) { + UNREFERENCED_PARAMETER(dummy); + + StartShellServer(); + + return 0; +} + +VOID SvcInit(DWORD dwArgc, LPTSTR* lpszArgv) { + ghSvcStopEvent = CreateEvent(NULL, // default security attributes + TRUE, // manual reset event + FALSE, // not signaled + NULL); // no name + + if (ghSvcStopEvent == NULL) { + ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 0); + return; + } + + // Report running status when initialization is complete. + ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); + + ghExecuteService = CreateThread(NULL, 0, ServerThreadStart, NULL, 0, NULL); + + while (1) { + // Check whether to stop the service. + WaitForSingleObject(ghSvcStopEvent, INFINITE); + TerminateThread(ghExecuteService, 0); + ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); + return; + } +} diff --git a/service.c b/service.c new file mode 100644 index 0000000..13ba002 --- /dev/null +++ b/service.c @@ -0,0 +1,149 @@ +#include "service.h" + +extern SERVICE_STATUS gSvcStatus; +extern SERVICE_STATUS_HANDLE gSvcStatusHandle; +extern HANDLE ghSvcStopEvent; + +VOID SvcInstall() { + SC_HANDLE schSCManager; + SC_HANDLE schService; + TCHAR szUnquotedPath[MAX_PATH]; + + if (!GetModuleFileName(NULL, szUnquotedPath, MAX_PATH)) { + printf("Cannot install service (%d)\n", GetLastError()); + return; + } + + TCHAR szPath[MAX_PATH]; + StringCbPrintf(szPath, MAX_PATH, TEXT("\"%s\" -sc"), szUnquotedPath); + + // Get a handle to the SCM database. + schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + + if (NULL == schSCManager) { + printf("OpenSCManager failed (%d)\n", GetLastError()); + return; + } + + printf("Installing %s as service...\n", szPath); + + schService = CreateService(schSCManager, // SCM database + SVCNAME, // name of service + SVCNAME, // service name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_DEMAND_START, // start type + SERVICE_ERROR_NORMAL, // error control type + szPath, // path to service's binary + NULL, // no load ordering group + NULL, // no tag identifier + NULL, // no dependencies + NULL, // LocalSystem account + NULL); // no password + + if (schService == NULL) { + printf("CreateService failed (%d)\n", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } + else + printf("Service installed successfully\n"); + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); +} + +VOID ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, + DWORD dwWaitHint) { + static DWORD dwCheckPoint = 1; + + // Fill in the SERVICE_STATUS structure. + gSvcStatus.dwCurrentState = dwCurrentState; + gSvcStatus.dwWin32ExitCode = dwWin32ExitCode; + gSvcStatus.dwWaitHint = dwWaitHint; + + if (dwCurrentState == SERVICE_START_PENDING) + gSvcStatus.dwControlsAccepted = 0; + else + gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + if ((dwCurrentState == SERVICE_RUNNING) || + (dwCurrentState == SERVICE_STOPPED)) + gSvcStatus.dwCheckPoint = 0; + else + gSvcStatus.dwCheckPoint = dwCheckPoint++; + + // Report the status of the service to the SCM. + SetServiceStatus(gSvcStatusHandle, &gSvcStatus); +} + +VOID WINAPI SvcCtrlHandler(DWORD dwCtrl) { + // Handle the requested control code. + switch (dwCtrl) { + case SERVICE_CONTROL_STOP: + ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); + + // Signal the service to stop. + SetEvent(ghSvcStopEvent); + ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0); + + return; + + case SERVICE_CONTROL_INTERROGATE: + break; + + default: + break; + } +} + +VOID SvcReportEvent(LPTSTR szFunction) { + HANDLE hEventSource; + LPCTSTR lpszStrings[2]; + TCHAR Buffer[80]; + + hEventSource = RegisterEventSource(NULL, SVCNAME); + + if (NULL != hEventSource) { + StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, + GetLastError()); + + lpszStrings[0] = SVCNAME; + lpszStrings[1] = Buffer; + + ReportEvent(hEventSource, // event log handle + EVENTLOG_ERROR_TYPE, // event type + 0, // event category + SVC_ERROR, // event identifier + NULL, // no security identifier + 2, // size of lpszStrings array + 0, // no binary data + lpszStrings, // array of strings + NULL); // no binary data + + DeregisterEventSource(hEventSource); + } +} + +VOID SvcRemove(void) { + SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (!hSCManager) { + printf("Error: Can't open Service Control Manager\n"); + // addLogMessage("Error: Can't open Service Control Manager"); + return; + } + + SC_HANDLE hService = OpenService(hSCManager, SVCNAME, SERVICE_STOP | DELETE); + if (!hService) { + printf("Error: Can't remove service\n"); + // addLogMessage("Error: Can't remove service"); + CloseServiceHandle(hSCManager); + return; + } + + DeleteService(hService); + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + // addLogMessage("Success remove service!"); + printf("Service deleted successfully\n"); +} \ No newline at end of file diff --git a/service.h b/service.h new file mode 100644 index 0000000..728a212 --- /dev/null +++ b/service.h @@ -0,0 +1,16 @@ +#include +#include +#include + +#pragma comment(lib, "advapi32.lib") + +#define SVCNAME TEXT("ShellServer") +#define SVC_ERROR ((DWORD)0xC0020001L) + +VOID SvcInstall(void); +VOID WINAPI SvcCtrlHandler(DWORD); +VOID WINAPI SvcMain(DWORD, LPTSTR*); + +VOID ReportSvcStatus(DWORD, DWORD, DWORD); +VOID SvcInit(DWORD, LPTSTR*); +VOID SvcReportEvent(LPTSTR); \ No newline at end of file