A forum for reverse engineering, OS internals and malware analysis 

 #32554  by pointer
 Sat Feb 02, 2019 12:44 pm
I have a simple Delphi VCL form app that is executed in the NT AUTHORITY\SYSTEM account via a service app.
I have a batch file that will finalize the service and VCL processes and then delete the EXE files..

To execute the batch file, I'm using ShellExecute():
Code: Select all
procedure TForm1.Button1Click(Sender: TObject);
begin
 ShellExecute(0, 'open',
        PChar(IncludeTrailingBackslash(ExtractFilePath(ParamStr(0))) + 'delete.bat'),
        nil, PChar(ExtractFilePath(ParamStr(0))), SW_SHOWNORMAL);
end;
Here is the batch file:
Code: Select all
@echo off
set EXE=MyService.exe
set EXE2=Project1.exe
FOR /F %%x IN ('tasklist /NH /FI "IMAGENAME eq %EXE%"') DO IF %%x == %EXE% goto FOUND
FOR /F %%x IN ('tasklist /NH /FI "IMAGENAME eq %EXE2%"') DO IF %%x == %EXE2% goto FOUND2
:FOUND
taskkill /F /IM MyService.exe
ping 127.0.0.1 -n 1 > nul
del /f "*.exe"
ping 127.0.0.1 -n 1 > nul
del /f "*.txt"
del /f "*.cur"
ping 127.0.0.1 -n 1 > nul
del /f "*.bmp"
ping 127.0.0.1 -n 1 > nul
del /f "*.jpg"
ping 127.0.0.1 -n 1 > nul
del /f "*.bat"
cls
exit
:FOUND2
taskkill /F /IM Project1.exe
ping 127.0.0.1 -n 1 > nul
del /f "*.exe"
ping 127.0.0.1 -n 1 > nul
del /f "*.txt"
del /f "*.cur"
ping 127.0.0.1 -n 1 > nul
del /f "*.bmp"
ping 127.0.0.1 -n 1 > nul
del /f "*.jpg"
ping 127.0.0.1 -n 1 > nul
del /f "*.bat"
cls
exit
The trouble is when I call ShellExecute() from the VCL app, a cmd.exe window appears but nothing happens (like if something locks the execution of code inside the batch file).

This is what happens when all is executed:

Image

The code above is complete and can be tested executing the vcl app.exe on NT AUTHORITY\SYSTEM account using Process Hacker tool.
 #32561  by Brock
 Mon Feb 04, 2019 3:55 am
What does the code look like which spawns the VCL app from the service? The VCL app might have issues with the user environment.
Post your code for executing the VCL app from the service, I assume in the service you're using CreateProcessAsUser() or similar?

* Basically, ShellExecute() isn't an API that should be used for the system environment, but such hacks are likely possible through ShellExecuteEx() or with environment updating.

See below

https://textslashplain.com/2018/10/11/s ... te-doesnt/
 #32563  by pointer
 Fri Feb 08, 2019 1:26 pm
Brock wrote: Mon Feb 04, 2019 3:55 am What does the code look like which spawns the VCL app from the service? The VCL app might have issues with the user environment.
Post your code for executing the VCL app from the service, I assume in the service you're using CreateProcessAsUser() or similar?

* Basically, ShellExecute() isn't an API that should be used for the system environment, but such hacks are likely possible through ShellExecuteEx() or with environment updating.

See below

https://textslashplain.com/2018/10/11/s ... te-doesnt/
@Brock, thank you.

Here is the code of service:
Code: Select all
// MyService.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "resource.h"
#include <Windows.h>
#include <WtsApi32.h>
#include <UserEnv.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <winternl.h>
#include <psapi.h>
#include <iostream>
#include <stdio.h>

using namespace std;

#pragma comment(lib, "WtsApi32.lib")
#pragma comment(lib, "UserEnv.lib")
#pragma comment(lib, "shlwapi.lib")

#define MY_SERVICE_NAME L"MyService"
#define MY_SERVICE_DESCRIPTOR L"MyService description"
#define MY_SERVICE_BIN_NAME L"MyService.exe"
#define MY_APPLICATION_BIN_NAME L"Project1.exe"

enum WINSTA0_DESKTOP
{
    WINSTA0_DEFAULT,
    WINSTA0_WINLOGON
};

BOOL StartSystemUserProcess(
    wchar_t *pszCmd,
    wchar_t *pszParam,
    WINSTA0_DESKTOP winstaDesktop,
    DWORD *pdwExitCode,
    BOOL bWaitTerm,
    DWORD dwWaitMs)
{
    wchar_t szCmd[MAX_PATH];
    DWORD dwDirSize = sizeof(szCmd);
    STARTUPINFO si;
    BOOL bResult = FALSE;
    BOOL bReturn = FALSE;
    DWORD dwSessionId = 0xf, winlogonPid = 0xf;
    HANDLE hUserToken, hUserTokenDup, hPToken, hProcess;
    DWORD dwCreationFlags;
    PROCESSENTRY32 procEntry;
    PROCESS_INFORMATION pi;

    StringCchPrintf(szCmd, MAX_PATH, L"\"%s\" %s", pszCmd, pszParam);
    dwSessionId = WTSGetActiveConsoleSessionId();
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (hSnap == INVALID_HANDLE_VALUE) return FALSE;

    procEntry.dwSize = sizeof(PROCESSENTRY32);

    if (!Process32First(hSnap, &procEntry)) return FALSE;

    do  {
        if (_wcsicmp(procEntry.szExeFile, L"winlogon.exe") == 0) {

            DWORD winlogonSessId = 0;

            if (ProcessIdToSessionId(procEntry.th32ProcessID, &winlogonSessId)
                && winlogonSessId == dwSessionId) {
                winlogonPid = procEntry.th32ProcessID;
                break;
            }
        }
    } while (Process32Next(hSnap, &procEntry));

    WTSQueryUserToken(dwSessionId, &hUserToken);
    dwCreationFlags = DEBUG_PROCESS;
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);

    switch (winstaDesktop) {
    case WINSTA0_DEFAULT:
        si.lpDesktop = L"winsta0\\default";
        break;
    case WINSTA0_WINLOGON:
        si.lpDesktop = L"winsta0\\WinLogon";
        break;
    default:
        si.lpDesktop = L"winsta0\\default";
        break;
    }

    ZeroMemory(&pi, sizeof(pi));
    TOKEN_PRIVILEGES tp;
    LUID luid;

    hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, winlogonPid);

    bResult = OpenProcessToken(
        hProcess,
        TOKEN_ADJUST_PRIVILEGES |
        TOKEN_QUERY |
        TOKEN_DUPLICATE |
        TOKEN_ASSIGN_PRIMARY |
        TOKEN_ADJUST_SESSIONID |
        TOKEN_READ |
        TOKEN_WRITE,
        &hPToken);

    if (!bResult) {
        printf("\n OpenProcessToken error - %d \n", GetLastError());
    }

    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
        printf("\n LookupPrivilegeValue error - %d \n", GetLastError());
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hUserTokenDup);
    SetTokenInformation(hUserTokenDup, TokenSessionId, (void*)dwSessionId, sizeof(DWORD));

    bResult = AdjustTokenPrivileges(
        hUserTokenDup,
        FALSE,
        &tp,
        sizeof(TOKEN_PRIVILEGES),
        (PTOKEN_PRIVILEGES)NULL,
        NULL);

    if (!bResult) {
        printf("\n AdjustTokenPrivileges error - %d \n", GetLastError());
    }

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
        printf("\n ERROR_NOT_ALL_ASSIGNED: Token does not have the privilege. \n");
    }

    LPVOID pEnv = NULL;

    if (CreateEnvironmentBlock(&pEnv, hUserTokenDup, TRUE)) {
        dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
    }
    else {
        pEnv = NULL;
    }

    bResult = CreateProcessAsUser(
        hUserTokenDup,
        NULL,
        szCmd,
        NULL,
        NULL,
        FALSE,
        dwCreationFlags,
        pEnv,
        NULL,
        &si,
        &pi);

    bReturn = bResult ? TRUE : FALSE;

    if (bWaitTerm == TRUE) {
        if (WaitForSingleObject(pi.hProcess, dwWaitMs) == WAIT_OBJECT_0) {
            if (pdwExitCode) {
                GetExitCodeProcess(pi.hProcess, pdwExitCode);
            }
        }
    }

    CloseHandle(pi.hThread);
    CloseHandle(hProcess);
    CloseHandle(hUserToken);
    CloseHandle(hUserTokenDup);
    CloseHandle(hPToken);

    //===========================================================================================================================================

    int Stop = 0;
    DEBUG_EVENT DebugEv = { 0 };
    DWORD dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;

    while (!Stop)
    {
        WaitForDebugEvent(&DebugEv, INFINITE);

        switch (DebugEv.dwDebugEventCode)
        {
        case EXCEPTION_DEBUG_EVENT:
        {
              switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode)
              {
              case EXCEPTION_ACCESS_VIOLATION:
                  break;
              case EXCEPTION_BREAKPOINT:
                  break;
              case EXCEPTION_DATATYPE_MISALIGNMENT:
                  break;
              case EXCEPTION_SINGLE_STEP:
                  break;
              case DBG_CONTROL_C:
                  break;
              default:
                  break;
              }
              break;
        }
        case CREATE_THREAD_DEBUG_EVENT:
            break;
        case CREATE_PROCESS_DEBUG_EVENT:
        {
           if (DebugEv.u.CreateProcessInfo.hFile)
               CloseHandle(DebugEv.u.CreateProcessInfo.hFile);
        }
            break;
        case EXIT_THREAD_DEBUG_EVENT:
            break;
        case EXIT_PROCESS_DEBUG_EVENT:
            Stop = 1;
            break;
        case LOAD_DLL_DEBUG_EVENT:
        {
         if (DebugEv.u.LoadDll.hFile)
             CloseHandle(DebugEv.u.LoadDll.hFile);
        }
            break;
        case UNLOAD_DLL_DEBUG_EVENT:
            break;
        case OUTPUT_DEBUG_STRING_EVENT:
            break;
        }

        ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, dwContinueStatus);

    }

    CloseHandle(pi.hProcess);

    return bReturn;
}

BOOL InstallMyService()
{
    WCHAR strServ[MAX_PATH] = { 0 };
    SC_HANDLE schSCManager;
    SC_HANDLE schService;
    LPCTSTR lpszBinaryPathName;

    GetModuleFileName(NULL, strServ, MAX_PATH);
    PathRemoveFileSpec(strServ);
    wcscat(strServ, L"\\"MY_SERVICE_BIN_NAME);

    if ((schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL)
        return (TRUE);
    lpszBinaryPathName = strServ;
    schService = CreateService(schSCManager, MY_SERVICE_NAME, MY_SERVICE_DESCRIPTOR,
        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
        lpszBinaryPathName, NULL, NULL, NULL, NULL, NULL);
    if (schService == NULL)
        return (FALSE);
    if (schService)
    {
        StartService(schService, 0, NULL);
    }
    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
    return (TRUE);
}

BOOL DeleteMyService()
{
    SC_HANDLE schSCManager;
    SC_HANDLE hService;

    if ((schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL)
        return (FALSE);
    if ((hService = OpenService(schSCManager, MY_SERVICE_NAME, SERVICE_ALL_ACCESS)) == NULL)
        return (FALSE);
    if (!DeleteService(hService))
        return (FALSE);
    if (!CloseServiceHandle(hService))
        return (FALSE);
    if (!CloseServiceHandle(schSCManager))
        return (FALSE);
    return (TRUE);
}

bool IsServiceInstalled(LPWSTR ServiceName)
{
    bool serviceInstalled = false;
    SC_HANDLE scm_handle = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
    if (scm_handle)
    {
        SC_HANDLE service_handle = OpenService(scm_handle, ServiceName, SERVICE_INTERROGATE);
        if (service_handle != NULL)
        {
            wprintf(L"Service  Installed\n");
            serviceInstalled = true;
            CloseServiceHandle(service_handle);
        }
        else
        {
            wprintf(_T("OpenService failed - service not installed\n"));
        }
        CloseServiceHandle(scm_handle);
    }
    else
        wprintf(_T("OpenService couldn't open - service not installed\n"));

    return serviceInstalled;

}

SERVICE_STATUS g_ServiceStatus;
SERVICE_STATUS_HANDLE g_ServiceStatusHandle;
BOOL bRunning = true;

void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
    switch (Opcode)
    {
    case SERVICE_CONTROL_PAUSE:
        g_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
        break;
    case SERVICE_CONTROL_CONTINUE:
        g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
        break;
    case SERVICE_CONTROL_STOP:
        g_ServiceStatus.dwWin32ExitCode = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        g_ServiceStatus.dwCheckPoint = 0;
        g_ServiceStatus.dwWaitHint = 0;
        SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
        bRunning = false;
        break;
    case SERVICE_CONTROL_INTERROGATE:
        break;
    default:
        break;
    }
}

void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
    g_ServiceStatus.dwServiceType = SERVICE_WIN32;
    g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwServiceSpecificExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;
    g_ServiceStatus.dwWaitHint = 0;
    g_ServiceStatusHandle = RegisterServiceCtrlHandler(MY_SERVICE_NAME, ServiceCtrlHandler);
    if (g_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)0)
        return;
    g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    g_ServiceStatus.dwCheckPoint = 0;
    g_ServiceStatus.dwWaitHint = 0;
    SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);

    //============================================================================================

    WCHAR strApp[MAX_PATH] = { 0 };
    GetModuleFileName(NULL, strApp, MAX_PATH);
    PathRemoveFileSpec(strApp);

    wcscat(strApp, L"\\"MY_APPLICATION_BIN_NAME);

    StartSystemUserProcess(strApp, L"", WINSTA0_DESKTOP::WINSTA0_DEFAULT, 0, FALSE, 0);

    //============================================================================================

    bRunning = true;
    while (bRunning)
    {
        Sleep(3000);
    }

}

int main(int argc, char* argv[])
{
    HWND hWnd = GetConsoleWindow();
    ShowWindow(hWnd, SW_HIDE);

    if (!IsServiceInstalled(MY_SERVICE_NAME)){

        if (InstallMyService())
            printf("\nService correctly installed\n");
        else
            printf("\nService uncorrectly installed\n");
    }

    else
    {
        SERVICE_TABLE_ENTRY DispatchTable[] = { { MY_SERVICE_NAME, ServiceMain }, { NULL, NULL } };
        StartServiceCtrlDispatcher(DispatchTable);
    }

    return (EXIT_SUCCESS);
}
I solved this replacing:
Code: Select all
dwCreationFlags = DEBUG_PROCESS;
by:
Code: Select all
dwCreationFlags = DEBUG_ONLY_THIS_PROCESS;