Code:
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#pragma hdrstop
TCHAR* serviceName = TEXT("Hello World Service");
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
HANDLE stopServiceEvent = 0;
static BOOL
getAndAllocateLogonSid(
HANDLE hToken,
PSID *pLogonSid
)
{
PTOKEN_GROUPS ptgGroups = NULL;
DWORD cbBuffer = 0; /* allocation size */
DWORD dwSidLength; /* required size to hold Sid */
UINT i; /* Sid index counter */
BOOL bSuccess = FALSE; /* assume this function will fail */
*pLogonSid = NULL; // invalidate pointer
/*
** Get neccessary memory allocation
*/
GetTokenInformation(hToken, TokenGroups, ptgGroups, cbBuffer, &cbBuffer);
if (cbBuffer)
ptgGroups = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbBuffer);
/*
** Get Sids for all groups the user belongs to
*/
bSuccess = GetTokenInformation(
hToken,
TokenGroups,
ptgGroups,
cbBuffer,
&cbBuffer
);
if (bSuccess == FALSE)
goto finish3;
/*
** Get the logon Sid by looping through the Sids in the token
*/
for(i = 0 ; i < ptgGroups->GroupCount ; i++)
{
if (ptgGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID)
{
/*
** insure we are dealing with a valid Sid
*/
bSuccess = IsValidSid(ptgGroups->Groups[i].Sid);
if (bSuccess == FALSE)
goto finish3;
/*
** get required allocation size to copy the Sid
*/
dwSidLength=GetLengthSid(ptgGroups->Groups[i].Sid);
/*
** allocate storage for the Logon Sid
*/
if(
(*pLogonSid = (PSID)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwSidLength))
==
NULL
)
{
bSuccess = FALSE;
goto finish3;
}
/*
** copy the Logon Sid to the storage we just allocated
*/
bSuccess = CopySid(
dwSidLength,
*pLogonSid,
ptgGroups->Groups[i].Sid
);
break;
}
}
finish3:
/*
** free allocated resources
*/
if (bSuccess == FALSE)
{
if(*pLogonSid != NULL)
{
HeapFree(GetProcessHeap(), 0, *pLogonSid);
*pLogonSid = NULL;
}
}
if (ptgGroups != NULL)
HeapFree(GetProcessHeap(), 0, ptgGroups);
return bSuccess;
}
static BOOL
setSidOnAcl(
PSID pSid,
PACL pAclSource,
PACL *pAclDestination,
DWORD AccessMask,
BOOL bAddSid,
BOOL bFreeOldAcl
)
{
ACL_SIZE_INFORMATION AclInfo;
DWORD dwNewAclSize;
LPVOID pAce;
DWORD AceCounter;
BOOL bSuccess=FALSE;
/*
** If we were given a NULL Acl, just provide a NULL Acl
*/
if (pAclSource == NULL)
{
*pAclDestination = NULL;
return TRUE;
}
if (!IsValidSid(pSid)) return FALSE;
/*
** Get ACL's parameters
*/
if (
!GetAclInformation(
pAclSource,
&AclInfo,
sizeof(ACL_SIZE_INFORMATION),
AclSizeInformation
)
)
return FALSE;
/*
** Compute size for new ACL, based on
** addition or subtraction of ACE
*/
if (bAddSid)
{
dwNewAclSize = AclInfo.AclBytesInUse +
sizeof(ACCESS_ALLOWED_ACE) +
GetLengthSid(pSid) -
sizeof(DWORD) ;
}
else
{
dwNewAclSize = AclInfo.AclBytesInUse -
sizeof(ACCESS_ALLOWED_ACE) -
GetLengthSid(pSid) +
sizeof(DWORD) ;
}
*pAclDestination = (PACL) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwNewAclSize);
if(*pAclDestination == NULL)
return FALSE;
/*
** initialize new Acl
*/
bSuccess = InitializeAcl(*pAclDestination, dwNewAclSize, ACL_REVISION);
if (bSuccess == FALSE)
goto finish5;
/*
** copy existing ACEs to new ACL
*/
for(AceCounter = 0 ; AceCounter < AclInfo.AceCount ; AceCounter++)
{
/*
** fetch existing ace
*/
bSuccess = GetAce(pAclSource, AceCounter, &pAce);
if (bSuccess == FALSE)
goto finish5;
/*
** check to see if we are removing the ACE
*/
if (!bAddSid)
{
/*
** we only care about ACCESS_ALLOWED ACEs
*/
if ((((PACE_HEADER)pAce)->AceType) == ACCESS_ALLOWED_ACE_TYPE)
{
PSID pTempSid=(PSID)&((PACCESS_ALLOWED_ACE)pAce)->SidStart;
/*
** if the Sid matches, skip adding this Sid
*/
if (EqualSid(pSid, pTempSid)) continue;
}
}
/*
** append ACE to ACL
*/
bSuccess = AddAce(
*pAclDestination,
ACL_REVISION,
0, // maintain Ace order
pAce,
((PACE_HEADER)pAce)->AceSize
);
if (bSuccess == FALSE)
goto finish5;
}
/*
** If appropriate, add ACE representing pSid
*/
if (bAddSid)
bSuccess = AddAccessAllowedAce(
*pAclDestination,
ACL_REVISION,
AccessMask,
pSid
);
finish5:
/*
** free memory if an error occurred
*/
if (!bSuccess)
{
if(*pAclDestination != NULL)
HeapFree(GetProcessHeap(), 0, *pAclDestination);
}
else if (bFreeOldAcl)
HeapFree(GetProcessHeap(), 0, pAclSource);
return bSuccess;
}
static BOOL
setWinstaDesktopSecurity(
HWINSTA hWinsta,
HDESK hDesktop,
PSID pLogonSid,
BOOL bGrant,
HANDLE hToken
)
{
SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
PSECURITY_DESCRIPTOR sdDesktop = NULL;
PSECURITY_DESCRIPTOR sdWinsta = NULL;
SECURITY_DESCRIPTOR sdNewDesktop;
SECURITY_DESCRIPTOR sdNewWinsta;
DWORD sdDesktopLength = 0; /* allocation size */
DWORD sdWinstaLength = 0; /* allocation size */
PACL pDesktopDacl; /* previous Dacl on Desktop */
PACL pWinstaDacl; /* previous Dacl on Winsta */
PACL pNewDesktopDacl = NULL; /* new Dacl for Desktop */
PACL pNewWinstaDacl = NULL; /* new Dacl for Winsta */
BOOL bDesktopDaclPresent;
BOOL bWinstaDaclPresent;
BOOL bDaclDefaultDesktop;
BOOL bDaclDefaultWinsta;
BOOL bSuccess = FALSE;
PSID pUserSid = NULL;
/*
** Obtain security descriptor for Desktop
*/
GetUserObjectSecurity(
hDesktop,
&si,
sdDesktop,
sdDesktopLength,
&sdDesktopLength
);
if (sdDesktopLength)
sdDesktop = (PSECURITY_DESCRIPTOR)HeapAlloc(
GetProcessHeap(), HEAP_ZERO_MEMORY, sdDesktopLength);
bSuccess = GetUserObjectSecurity(
hDesktop,
&si,
sdDesktop,
sdDesktopLength,
&sdDesktopLength
);
if (bSuccess == FALSE)
goto finish4;
/*
** Obtain security descriptor for Window station
*/
GetUserObjectSecurity(
hWinsta,
&si,
sdWinsta,
sdWinstaLength,
&sdWinstaLength
);
if (sdWinstaLength)
sdWinsta = (PSECURITY_DESCRIPTOR)HeapAlloc(
GetProcessHeap(), HEAP_ZERO_MEMORY, sdWinstaLength);
bSuccess = GetUserObjectSecurity(
hWinsta,
&si,
sdWinsta,
sdWinstaLength,
&sdWinstaLength
);
if (bSuccess == FALSE)
goto finish4;
/*
** Obtain DACL from security descriptor for desktop
*/
bSuccess = GetSecurityDescriptorDacl(
sdDesktop,
&bDesktopDaclPresent,
&pDesktopDacl,
&bDaclDefaultDesktop
);
if (bSuccess == FALSE)
goto finish4;
/*
** Obtain DACL from security descriptor for Window station
*/
bSuccess = GetSecurityDescriptorDacl(
sdWinsta,
&bWinstaDaclPresent,
&pWinstaDacl,
&bDaclDefaultWinsta
);
if (bSuccess == FALSE)
goto finish4;
/*
** Create new DACL with Logon and User Sid for Desktop
*/
if(bDesktopDaclPresent) {
bSuccess = setSidOnAcl(
pLogonSid,
pDesktopDacl,
&pNewDesktopDacl,
GENERIC_READ | GENERIC_WRITE | READ_CONTROL
| DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW
| DESKTOP_CREATEMENU | DESKTOP_SWITCHDESKTOP
| DESKTOP_ENUMERATE,
bGrant,
FALSE
);
if (bSuccess == FALSE)
goto finish4;
}
/*
** Create new DACL with Logon and User Sid for Window station
*/
if(bWinstaDaclPresent)
{
bSuccess = setSidOnAcl(
pLogonSid,
pWinstaDacl,
&pNewWinstaDacl,
GENERIC_READ | GENERIC_WRITE | READ_CONTROL
| WINSTA_ACCESSGLOBALATOMS
| WINSTA_ENUMDESKTOPS | WINSTA_READATTRIBUTES
| WINSTA_ACCESSCLIPBOARD | WINSTA_ENUMERATE
| WINSTA_EXITWINDOWS,
bGrant,
FALSE
);
if (bSuccess == FALSE)
goto finish4;
}
/*
** Initialize the target security descriptor for Desktop
*/
if (bDesktopDaclPresent)
{
bSuccess = InitializeSecurityDescriptor(
&sdNewDesktop,
SECURITY_DESCRIPTOR_REVISION
);
if (bSuccess == FALSE)
goto finish4;
}
/*
** Initialize the target security descriptor for Window station
*/
if(bWinstaDaclPresent)
{
bSuccess = InitializeSecurityDescriptor(
&sdNewWinsta,
SECURITY_DESCRIPTOR_REVISION
);
if (bSuccess == FALSE)
goto finish4;
}
/*
** Apply new ACL to the Desktop security descriptor
*/
if(bDesktopDaclPresent)
{
bSuccess = SetSecurityDescriptorDacl(
&sdNewDesktop,
TRUE,
pNewDesktopDacl,
bDaclDefaultDesktop
);
if (bSuccess == FALSE)
goto finish4;
}
/*
** Apply new ACL to the Window station security descriptor
*/
if(bWinstaDaclPresent)
{
bSuccess = SetSecurityDescriptorDacl(
&sdNewWinsta,
TRUE,
pNewWinstaDacl,
bDaclDefaultWinsta
);
if (bSuccess == FALSE)
goto finish4;
}
/*
** Apply security descriptors with new DACLs to Desktop and Window station
*/
if (bDesktopDaclPresent)
{
bSuccess = SetUserObjectSecurity(
hDesktop,
&si,
&sdNewDesktop
);
if (bSuccess == FALSE)
goto finish4;
}
if(bWinstaDaclPresent)
bSuccess = SetUserObjectSecurity(
hWinsta,
&si,
&sdNewWinsta
);
if (bSuccess == FALSE)
goto finish4;
finish4:
if (sdDesktop != NULL)
HeapFree(GetProcessHeap(), 0, sdDesktop);
if (sdWinsta != NULL)
HeapFree(GetProcessHeap(), 0, sdWinsta);
if (pNewDesktopDacl != NULL)
HeapFree(GetProcessHeap(), 0, pNewDesktopDacl);
if (pNewWinstaDacl != NULL)
HeapFree(GetProcessHeap(), 0, pNewWinstaDacl);
return bSuccess;
}
static BOOL
allowDesktopAccess(HANDLE hToken)
{
HWINSTA hWinsta = NULL;
HDESK hDesktop = NULL;
PSID pLogonSid = NULL;
BOOL ok = FALSE;
if (!getAndAllocateLogonSid(hToken, &pLogonSid))
return FALSE;
hWinsta=GetProcessWindowStation();
hDesktop=GetThreadDesktop(GetCurrentThreadId());
ok = SetHandleInformation(hDesktop,
HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
if (!ok)
return FALSE;
ok = SetHandleInformation(hWinsta,
HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
if (!ok)
return FALSE;
ok = setWinstaDesktopSecurity(hWinsta, hDesktop, pLogonSid, TRUE, hToken);
if(pLogonSid != NULL)
HeapFree(GetProcessHeap(), 0, pLogonSid);
return ok;
}
void WINAPI ServiceControlHandler( DWORD controlCode )
{
switch ( controlCode )
{
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
SetEvent( stopServiceEvent );
return;
case SERVICE_CONTROL_PAUSE:
break;
case SERVICE_CONTROL_CONTINUE:
break;
default:
if ( controlCode >= 128 && controlCode <= 255 )
// User defined control code
break;
else
// unrecognised control code
break;
}
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
#define COMMAND "NOTEPAD.EXE"
#define USER "John Doe"
#define DOMAIN "."
#define PASSWORD "johndoe"
void WINAPI ServiceMain( DWORD argc, TCHAR* argv[] )
{
HANDLE hToken = NULL;
BOOL ok = FALSE;
STARTUPINFO startup_info;
PROCESS_INFORMATION proc_info;
LPTSTR lpCmd = NULL;
char lpSystemDir[MAX_PATH];
// initialise service status
serviceStatus.dwServiceType = SERVICE_WIN32;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
serviceStatus.dwControlsAccepted = 0;
serviceStatus.dwWin32ExitCode = NO_ERROR;
serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
serviceStatus.dwCheckPoint = 0;
serviceStatus.dwWaitHint = 0;
serviceStatusHandle = RegisterServiceCtrlHandler ( serviceName, ServiceControlHandler );
if ( serviceStatusHandle )
{
// service is starting
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus ( serviceStatusHandle, &serviceStatus );
// do initialisation here
stopServiceEvent = CreateEvent( 0, FALSE, FALSE, 0 );
//running
serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
ok = LogonUser(
TEXT(USER),
TEXT(DOMAIN),
TEXT(PASSWORD),
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
&hToken
);
if (ok == FALSE)
goto finish2;
ok = allowDesktopAccess(hToken);
if (ok == FALSE)
goto finish2;
ZeroMemory(&startup_info,sizeof(STARTUPINFO));
startup_info.cb = sizeof(STARTUPINFO);
startup_info.wShowWindow = SW_SHOW;
startup_info.dwFlags = STARTF_USESTDHANDLES;
startup_info.lpDesktop = (LPTSTR) "Winsta0\\Default";
ok = GetSystemDirectory(lpSystemDir, MAX_PATH);
if (ok > 0)
{
lpCmd = (char *) LocalAlloc(LPTR, ok + strlen(COMMAND) + 2);
sprintf(lpCmd, "%s\\%s", lpSystemDir, COMMAND);
}
else
goto finish2;
ok = CreateProcessAsUser(
hToken,
NULL,
lpCmd,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&startup_info,
&proc_info);
if (ok == TRUE)
{
CloseHandle(proc_info.hThread);
CloseHandle(proc_info.hProcess);
}
finish2:
if (ok == FALSE)
{
LPTSTR lpMsgBuf;
DWORD cnBytes;
cnBytes = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)&lpMsgBuf,
0,
NULL
);
if (cnBytes > 0)
{
MessageBox(NULL, lpMsgBuf, "SECBUG", MB_OK);
LocalFree(lpMsgBuf);
}
}
if (lpCmd != NULL)
LocalFree(lpCmd);
if (hToken != NULL)
CloseHandle(hToken);
do { // nothing
} while (WaitForSingleObject( stopServiceEvent, 5000 ) == WAIT_TIMEOUT );
// service was stopped
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
// do cleanup here
CloseHandle( stopServiceEvent );
stopServiceEvent = 0;
// service is now stopped
serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
}
void RunService()
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ serviceName, ServiceMain },
{ 0, 0 }
};
StartServiceCtrlDispatcher ( serviceTable );
}
void InstallService()
{
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CREATE_SERVICE );
if ( serviceControlManager )
{
TCHAR path[ _MAX_PATH + 1];
if ( GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0 )
{
SC_HANDLE service = CreateService( serviceControlManager,
serviceName, serviceName,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path,
0, 0, 0, 0, 0);
if (service)
CloseServiceHandle( service );
}
CloseServiceHandle( serviceControlManager );
}
}
void UninstallService()
{
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CONNECT );
if (serviceControlManager )
{
SC_HANDLE service = OpenService( serviceControlManager,
serviceName, SERVICE_QUERY_STATUS | DELETE );
if ( service )
{
SERVICE_STATUS serviceStatus;
if ( QueryServiceStatus( service, &serviceStatus ) )
{
if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
DeleteService ( service );
}
CloseServiceHandle( service );
}
CloseServiceHandle( serviceControlManager );
}
}
int _tmain( int argc, TCHAR* argv[] )
{
if ( argc > 1 && lstrcmpi( argv[1], TEXT("install") ) == 0)
{
InstallService();
}
else if ( argc > 1 && lstrcmpi( argv[1], TEXT("uninstall") ) == 0 )
{
UninstallService();
}
else
{
RunService();
}
return 0;
}
Of course the hello world popup is a snap.