pastebin - collaborative debugging tool
rovema.kpaste.net RSS


Prototype runassystem.c - run Win32 program as Windows user "SYSTEM"
Posted by Anonymous on Tue 26th Aug 2025 10:45
raw | new post

  1.  
  2. /*
  3.  * MIT License
  4.  *
  5.  * Copyright (c) 2025 Roland Mainz <roland.mainz@nrubsig.org>
  6.  *
  7.  * Permission is hereby granted, free of charge, to any person obtaining a copy
  8.  * of this software and associated documentation files (the "Software"), to deal
  9.  * in the Software without restriction, including without limitation the rights
  10.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11.  * copies of the Software, and to permit persons to whom the Software is
  12.  * furnished to do so, subject to the following conditions:
  13.  *
  14.  * The above copyright notice and this permission notice shall be included in
  15.  * allcopies or substantial portions of the Software.
  16.  *
  17.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23.  * THE SOFTWARE.
  24.  */
  25.  
  26. /*
  27.  * runassystem.c - run Win32 program as Windows user "SYSTEM"
  28.  *
  29.  * Written by Roland Mainz <roland.mainz@nrubsig.org>
  30.  */
  31.  
  32. /*
  33.  * Compile with:
  34.  * $ clang -target x86_64-pc-windows-gnu -Wall -municode runassystem.c -lWtsapi32 -o runassystem.exe
  35.  */
  36.  
  37. #define WIN32_LEAN_AND_MEAN 1
  38.  
  39. #include <windows.h>
  40. #include <strsafe.h>
  41. #include <stdbool.h>
  42. #include <stdio.h>
  43. #include <wchar.h>
  44. #include <WtsApi32.h>
  45. #include <sddl.h>
  46. #include <fcntl.h>
  47.  
  48.  
  49. /* Global variables */
  50. static const wchar_t* SERVICE_NAME = L"RunAsSYSTEM_temporary_service0001";
  51. static const wchar_t* SERVICE_DISPLAY_NAME = L"My C System Process Launcher Service";
  52.  
  53. static SERVICE_STATUS g_ServiceStatus = {0};
  54. static SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
  55. static HANDLE g_ServiceStopEvent = INVALID_HANDLE_VALUE;
  56.  
  57. static int service_argc = 0;
  58. static wchar_t **service_argv = NULL;
  59.  
  60. /* Local prototypes */
  61. static void ReportError(const char* context);
  62. static void LaunchInteractiveProcess(void);
  63. static void WINAPI ServiceCtrlHandler(DWORD CtrlCode);
  64. static void WINAPI ServiceMain(DWORD argc, LPWSTR* argv);
  65.  
  66.  
  67. #define DBG 1
  68.  
  69. #ifdef DBG
  70. #define DbgP(_x_) RASDbgPrint _x_
  71. #else
  72. #define DbgP(_x_)
  73. #endif
  74. #define TRACE_TAG   L"[RAS]"
  75.  
  76. #define PTR2PTRDIFF_T(p) (((char *)(p))-((char *)0))
  77. #define HANDLE2INT(h) ((int)PTR2PTRDIFF_T(h))
  78.  
  79.  
  80. ULONG _cdecl RASDbgPrint(__in LPWSTR fmt, ...)
  81. {
  82.     DWORD saved_lasterr;
  83.     ULONG rc = 0;
  84. #define SZBUFFER_SIZE 1024
  85.     wchar_t szbuffer[SZBUFFER_SIZE+1];
  86.     wchar_t *szbp = szbuffer;
  87.  
  88.     saved_lasterr = GetLastError();
  89.  
  90.     va_list marker;
  91.     va_start(marker, fmt);
  92.  
  93.     (void)StringCchPrintfW(szbp, SZBUFFER_SIZE-(szbp - szbuffer),
  94.         TRACE_TAG L"[thr=%04x] ", (int)GetCurrentThreadId());
  95.     szbp += wcslen(szbp);
  96.  
  97.     (void)StringCchVPrintfW(szbp, SZBUFFER_SIZE-(szbp - szbuffer),
  98.         fmt, marker);
  99.     szbuffer[SZBUFFER_SIZE-1] = L'\0';
  100.  
  101.     OutputDebugStringW(szbuffer);
  102.  
  103.     va_end(marker);
  104.  
  105.     SetLastError(saved_lasterr);
  106.  
  107.     return rc;
  108. }
  109.  
  110. static
  111. void ReportError(const char* context)
  112. {
  113.     (void)fprintf(stderr,
  114.         "ERROR in %s: %d\n",
  115.         context,
  116.         (int)GetLastError());
  117. }
  118.  
  119. static
  120. void LaunchInteractiveProcess(void)
  121. {
  122.     STARTUPINFOA si;
  123.     PROCESS_INFORMATION pi;
  124.  
  125.     char buffer[16384];
  126.     char *s = buffer;
  127.  
  128.     HANDLE hFile_stdout = INVALID_HANDLE_VALUE;
  129.     HANDLE hFile_stderr = INVALID_HANDLE_VALUE;
  130.  
  131.     SECURITY_ATTRIBUTES sa = { 0 };
  132.  
  133.     (void)memset(&si, 0, sizeof(si));
  134.     (void)memset(&pi, 0, sizeof(pi));
  135.  
  136.     si.cb = sizeof(si);
  137.  
  138.     sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  139.     sa.bInheritHandle = TRUE;
  140.  
  141.     hFile_stdout = CreateFileA("C:\\hjk_stdout",
  142.         GENERIC_READ|GENERIC_WRITE,
  143.         FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  144.         &sa,
  145.         CREATE_ALWAYS,
  146.         FILE_ATTRIBUTE_TEMPORARY,
  147.         NULL);
  148.     if (hFile_stdout == INVALID_HANDLE_VALUE) {
  149.         DbgP((L"LaunchInteractiveProcess: cannot open stdout, lasterr=%d\n",
  150.             (int)GetLastError()));
  151.         goto done;
  152.     }
  153.     hFile_stderr = CreateFileA("C:\\hjk_stderr",
  154.         GENERIC_READ|GENERIC_WRITE,
  155.         FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  156.         &sa,
  157.         CREATE_ALWAYS,
  158.         FILE_ATTRIBUTE_TEMPORARY,
  159.         NULL);
  160.     if (hFile_stderr == INVALID_HANDLE_VALUE) {
  161.         DbgP((L"LaunchInteractiveProcess: cannot open stderr, lasterr=%d\n",
  162.             (int)GetLastError()));
  163.         goto done;
  164.     }
  165.  
  166.     (void)SetHandleInformation(hFile_stdout, HANDLE_FLAG_INHERIT, TRUE);
  167.     (void)SetHandleInformation(hFile_stderr, HANDLE_FLAG_INHERIT, TRUE);
  168.  
  169.     /*
  170.      * service_argv[0] == *.exe name
  171.      * service_argv[1] == "--service"
  172.      * service_argv[2] == <Service name>
  173.      */
  174.  
  175.     /* Command name + <space> separator */
  176.     s += sprintf(s, "%ls ", service_argv[3]);
  177.  
  178.     int i;
  179.     for (i=4 ; i < service_argc ; i++) {
  180.         s += sprintf(s, " \"%ls\"", service_argv[i]);
  181.     }
  182.  
  183.     si.dwFlags = STARTF_USESTDHANDLES;
  184.     si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  185.     si.hStdOutput = hFile_stdout;
  186.     si.hStdError = hFile_stderr;
  187.  
  188.     if (!CreateProcessA(NULL,
  189.         buffer,
  190.         NULL,
  191.         NULL,
  192.         TRUE,
  193.         0,
  194.         NULL,
  195.         NULL,
  196.         &si,
  197.         &pi)) {
  198.         (void)printf("CreateProcess failed (%d).\n", (int)GetLastError());
  199.         return;
  200.     }
  201.  
  202.     (void)WaitForSingleObject(pi.hProcess, INFINITE);
  203.  
  204.  
  205. done:
  206.     (void)CloseHandle(pi.hProcess);
  207.     (void)CloseHandle(pi.hThread);
  208.  
  209.     (void)CloseHandle(hFile_stdout);
  210.     (void)CloseHandle(hFile_stderr);
  211. }
  212.  
  213. static
  214. void WINAPI ServiceCtrlHandler(DWORD CtrlCode)
  215. {
  216.     switch (CtrlCode) {
  217.         case SERVICE_CONTROL_STOP:
  218.             if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
  219.                 break;
  220.  
  221.             g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
  222.             SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
  223.             SetEvent(g_ServiceStopEvent);
  224.             break;
  225.         default:
  226.             break;
  227.     }
  228. }
  229.  
  230. static
  231. void WINAPI ServiceMain(DWORD argc, wchar_t *argv[])
  232. {
  233.     g_StatusHandle = RegisterServiceCtrlHandlerW(service_argv[2], ServiceCtrlHandler);
  234.     if (!g_StatusHandle) {
  235.         ReportError("RegisterServiceCtrlHandlerW");
  236.         return;
  237.     }
  238.  
  239.     g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  240.     g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  241.     g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
  242.     SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
  243.  
  244.     g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  245.     if (g_ServiceStopEvent == NULL) {
  246.         g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
  247.         SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
  248.         return;
  249.     }
  250.  
  251.     g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
  252.     SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
  253.  
  254.     LaunchInteractiveProcess();
  255.  
  256.     (void)WaitForSingleObject(g_ServiceStopEvent, INFINITE);
  257.  
  258.     g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
  259.     SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
  260.     (void)CloseHandle(g_ServiceStopEvent);
  261. }
  262.  
  263. static
  264. bool InstallService(int argc, wchar_t *argv[])
  265. {
  266.     bool retval = false;
  267.     wchar_t szPath[MAX_PATH+1];
  268.     wchar_t szPathWithArg[16384];
  269.     wchar_t *s;
  270.     SC_HANDLE hSCManager;
  271.  
  272.     hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  273.     if (!hSCManager) {
  274.         ReportError("OpenSCManager");
  275.         return false;
  276.     }
  277.  
  278.     /* Get our *.exe name */
  279.     if (GetModuleFileNameW(NULL, szPath, MAX_PATH) == 0) {
  280.         ReportError("GetModuleFileNameW");
  281.         retval = false;
  282.         goto done;
  283.     }
  284.  
  285.     /* Manually construct the path with the argument for the service */
  286.     s = szPathWithArg;
  287.     s += swprintf(s, 1024, L"\"%ls\" --service %ls", szPath, SERVICE_NAME);
  288.     int i;
  289.     for (i=1 ; i < argc ; i++) {
  290.         /* FIXME: Quoting */
  291.         s += swprintf(s, 1024, L" \"%ls\"", argv[i]);
  292.     }
  293.  
  294.     /* Print arguments */
  295.     (void)wprintf(L"szPathWithArg='%ls'\n", szPathWithArg);
  296.  
  297.     /*
  298.      * FIXME: We should implement -u and -g to define username and primary
  299.      * group name
  300.      */
  301.     SC_HANDLE hService = CreateServiceW(hSCManager,
  302.         SERVICE_NAME, SERVICE_DISPLAY_NAME, SERVICE_ALL_ACCESS,
  303.         SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
  304.         szPathWithArg, NULL, NULL, NULL, L"NT AUTHORITY\\SYSTEM", NULL);
  305.     if (!hService) {
  306.         ReportError("CreateServiceW");
  307.         retval = false;
  308.         goto done;
  309.     }
  310.  
  311.     (void)wprintf(L"Service created successfully.\n");
  312.     if (!StartServiceW(hService, 0, NULL)) {
  313.         ReportError("StartServiceW");
  314.         retval = false;
  315.         goto done;
  316.     }
  317.    
  318.     (void)wprintf(L"Service started successfully.\n");
  319.  
  320.     /* Give service time to work before cleanup */
  321.     Sleep(2000);
  322.     (void)CloseServiceHandle(hService);
  323.     /* Success! */
  324.     retval = true;
  325.  
  326. done:
  327.     (void)CloseServiceHandle(hSCManager);
  328.     return retval;
  329. }
  330.  
  331. static
  332. void UninstallService(void)
  333. {
  334.     SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  335.     if (!hSCManager) {
  336.         ReportError("UninstallService: OpenSCManager");
  337.         return;
  338.     }
  339.  
  340.     SC_HANDLE hService = OpenServiceW(hSCManager, SERVICE_NAME, SERVICE_ALL_ACCESS);
  341.     if (!hService) {
  342.         ReportError("UninstallService: OpenServiceW");
  343.     } else {
  344.         SERVICE_STATUS status;
  345.  
  346.         ControlService(hService, SERVICE_CONTROL_STOP, &status);
  347.         (void)wprintf(L"Service stopped.\n");
  348.  
  349.         if (!DeleteService(hService)) {
  350.             ReportError("UninstallService: DeleteService");
  351.         } else {
  352.             (void)wprintf(L"UninstallService: Service deleted.\n");
  353.         }
  354.         (void)CloseServiceHandle(hService);
  355.     }
  356.  
  357.     (void)CloseServiceHandle(hSCManager);
  358. }
  359.  
  360. int wmain(int argc, wchar_t *argv[])
  361. {
  362.     int retval = EXIT_FAILURE;
  363.  
  364.     /*
  365.      * If started with "--service", run as a service
  366.      */
  367.     if ((argc > 2) && (wcscmp(argv[1], L"--service") == 0)) {
  368.         /*
  369.          * Store argument array passed to |wmain()| because the argument
  370.          * array for |ServiceMain()| does not have these arguments
  371.          */
  372.         service_argc = argc;
  373.         service_argv = argv;
  374.  
  375.         SERVICE_TABLE_ENTRYW ServiceTable[] = {
  376.             { .lpServiceName = argv[2], .lpServiceProc = ServiceMain },
  377.             { .lpServiceName = NULL,    .lpServiceProc = NULL }
  378.         };
  379.  
  380.         if (!StartServiceCtrlDispatcherW(ServiceTable)) {
  381.             ReportError("StartServiceCtrlDispatcherW");
  382.             return 1;
  383.         }
  384.         return 0;
  385.     }
  386.  
  387.     /* Otherwise, run as the client to manage the service */
  388.     (void)wprintf(L"Running as client to install and start the service...\n");
  389.  
  390.     /* Install and Start */
  391.     if (InstallService(argc, argv)) {
  392.         /* Stop and Uninstall */
  393.         UninstallService();
  394.  
  395.         (void)system("C:\\cygwin64\\bin\\bash.exe -c 'cat /cygdrive/c/hjk_stderr 1>&2'");
  396.         (void)system("C:\\cygwin64\\bin\\cat.exe /cygdrive/c/hjk_stdout");
  397.         (void)remove("/cygdrive/c/hjk_stderr");
  398.         (void)remove("/cygdrive/c/hjk_stdout");
  399.        
  400.         retval = EXIT_SUCCESS;
  401.     }
  402.  
  403.     return retval;
  404. }

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at