One approach, if you could access the process ID in an administrative context, is to deny the PROCESS_TERMINATE permission on the process to end users. Terminating the process (through task manager or other contexts) is by default granted to the owner, but can be explicitly denied. When it is denied, terminating the process would require the owner to manually change the ACL, and then terminate the process. If the user is neither an administrator nor the owner of the process, he will not be able to forcibly terminate the process (e.g., through Task Manager), although the process will be allowed to exit normally.
The following code puts an explicit deny ACE on the process with the PID processid
for members of the Everyone group.
#include "Aclapi.h"
#include "Sddl.h"
DWORD RestrictTerminateOnProcessId(DWORD processid)
{
PACL dacl = NULL, newdacl = NULL;
HANDLE ph = NULL;
PSECURITY_DESCRIPTOR* desc = NULL;
PSID everyonesid = NULL;
ph = OpenProcess(WRITE_DAC | READ_CONTROL, false, processid);
if (!ph) goto cleanup;
if (ERROR_SUCCESS != GetSecurityInfo(ph,
SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
&dacl,
NULL,
desc)) goto cleanup;
SID_IDENTIFIER_AUTHORITY WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
if (!AllocateAndInitializeSid(
&WorldAuth,1,SECURITY_WORLD_RID,
0,0,0,0,0,0,0,&everyonesid)) goto cleanup;
// begin copy dacl
_ACL_SIZE_INFORMATION si;
GetAclInformation(dacl,
&si,
sizeof(si),
AclSizeInformation);
DWORD dwNewAclSize = si.AclBytesInUse +
(2*sizeof(ACCESS_DENIED_ACE)) + (2*GetLengthSid(everyonesid)) -
(2*sizeof(DWORD));
newdacl = (PACL)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwNewAclSize);
if (newdacl == NULL) goto cleanup;
if (!InitializeAcl(newdacl, dwNewAclSize, ACL_REVISION_DS))
goto cleanup;
if (!AddAccessDeniedAce(newdacl,
ACL_REVISION_DS,
PROCESS_TERMINATE,
everyonesid)) goto cleanup;
for (int i = 0; i < si.AceCount; i++)
{
LPVOID pace = NULL;
if (!GetAce(dacl, i, &pace)) goto cleanup;
if (!AddAce(newdacl, ACL_REVISION_DS,
MAXDWORD, pace, ((PACE_HEADER)pace)->AceSize))
goto cleanup;
}
// end copy dacl
if (!SetSecurityInfo(ph,
SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
newdacl,
NULL)) goto cleanup;
SetLastError(0);
cleanup:
DWORD ret = GetLastError();
if (desc) LocalFree(desc);
if (newdacl) HeapFree(GetProcessHeap(), 0, (LPVOID)newdacl);
if (ph) CloseHandle(ph);
if (everyonesid) FreeSid(everyonesid);
return !ret;
}