Files
wj32 4c525afd5f ExtendedServices: Added auto-elevation when saving recovery information, triggers and other service settings
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@6131 21ef857c-d57f-4fe0-8362-d861dc6d29cd
2015-06-21 08:56:30 +00:00

1218 lines
36 KiB
C

/*
* Process Hacker -
* phsvc client
*
* Copyright (C) 2011-2015 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
#include <phapp.h>
#include <phsvccl.h>
HANDLE PhSvcClPortHandle;
PVOID PhSvcClPortHeap;
HANDLE PhSvcClServerProcessId;
NTSTATUS PhSvcConnectToServer(
_In_ PUNICODE_STRING PortName,
_In_opt_ SIZE_T PortSectionSize
)
{
NTSTATUS status;
HANDLE sectionHandle;
LARGE_INTEGER sectionSize;
PORT_VIEW clientView;
REMOTE_PORT_VIEW serverView;
SECURITY_QUALITY_OF_SERVICE securityQos;
ULONG maxMessageLength;
PHSVC_API_CONNECTINFO connectInfo;
ULONG connectInfoLength;
if (PhSvcClPortHandle)
return STATUS_ADDRESS_ALREADY_EXISTS;
if (PortSectionSize == 0)
PortSectionSize = 512 * 1024;
// Create the port section and connect to the port.
sectionSize.QuadPart = PortSectionSize;
status = NtCreateSection(
&sectionHandle,
SECTION_ALL_ACCESS,
NULL,
&sectionSize,
PAGE_READWRITE,
SEC_COMMIT,
NULL
);
if (!NT_SUCCESS(status))
return status;
clientView.Length = sizeof(PORT_VIEW);
clientView.SectionHandle = sectionHandle;
clientView.SectionOffset = 0;
clientView.ViewSize = PortSectionSize;
clientView.ViewBase = NULL;
clientView.ViewRemoteBase = NULL;
serverView.Length = sizeof(REMOTE_PORT_VIEW);
serverView.ViewSize = 0;
serverView.ViewBase = NULL;
securityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
securityQos.ImpersonationLevel = SecurityImpersonation;
securityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
securityQos.EffectiveOnly = TRUE;
connectInfoLength = sizeof(PHSVC_API_CONNECTINFO);
status = NtConnectPort(
&PhSvcClPortHandle,
PortName,
&securityQos,
&clientView,
&serverView,
&maxMessageLength,
&connectInfo,
&connectInfoLength
);
NtClose(sectionHandle);
if (!NT_SUCCESS(status))
return status;
PhSvcClServerProcessId = UlongToHandle(connectInfo.ServerProcessId);
// Create the port heap.
PhSvcClPortHeap = RtlCreateHeap(
HEAP_CLASS_1,
clientView.ViewBase,
clientView.ViewSize,
PAGE_SIZE,
NULL,
NULL
);
if (!PhSvcClPortHeap)
{
NtClose(PhSvcClPortHandle);
return STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}
VOID PhSvcDisconnectFromServer(
VOID
)
{
if (PhSvcClPortHeap)
{
RtlDestroyHeap(PhSvcClPortHeap);
PhSvcClPortHeap = NULL;
}
if (PhSvcClPortHandle)
{
NtClose(PhSvcClPortHandle);
PhSvcClPortHandle = NULL;
}
PhSvcClServerProcessId = NULL;
}
PVOID PhSvcpAllocateHeap(
_In_ SIZE_T Size,
_Out_ PULONG Offset
)
{
PVOID memory;
if (!PhSvcClPortHeap)
return NULL;
memory = RtlAllocateHeap(PhSvcClPortHeap, 0, Size);
if (!memory)
return NULL;
*Offset = (ULONG)((ULONG_PTR)memory - (ULONG_PTR)PhSvcClPortHeap);
return memory;
}
VOID PhSvcpFreeHeap(
_In_ PVOID Memory
)
{
if (!PhSvcClPortHeap)
return;
RtlFreeHeap(PhSvcClPortHeap, 0, Memory);
}
PVOID PhSvcpCreateString(
_In_opt_ PVOID String,
_In_ SIZE_T Length,
_Out_ PPH_RELATIVE_STRINGREF StringRef
)
{
PVOID memory;
SIZE_T length;
ULONG offset;
if (Length != -1)
length = Length;
else
length = PhCountStringZ(String) * sizeof(WCHAR);
if (length > MAXULONG32)
return NULL;
memory = PhSvcpAllocateHeap(length, &offset);
if (!memory)
return NULL;
if (String)
memcpy(memory, String, length);
StringRef->Length = (ULONG)length;
StringRef->Offset = offset;
return memory;
}
NTSTATUS PhSvcpCallServer(
_Inout_ PPHSVC_API_MSG Message
)
{
NTSTATUS status;
Message->h.u1.s1.DataLength = sizeof(PHSVC_API_MSG) - FIELD_OFFSET(PHSVC_API_MSG, p);
Message->h.u1.s1.TotalLength = sizeof(PHSVC_API_MSG);
Message->h.u2.ZeroInit = 0;
status = NtRequestWaitReplyPort(PhSvcClPortHandle, &Message->h, &Message->h);
if (!NT_SUCCESS(status))
return status;
return Message->p.ReturnStatus;
}
NTSTATUS PhSvcCallPlugin(
_In_ PPH_STRINGREF ApiId,
_In_reads_bytes_opt_(InLength) PVOID InBuffer,
_In_ ULONG InLength,
_Out_writes_bytes_opt_(OutLength) PVOID OutBuffer,
_In_ ULONG OutLength
)
{
NTSTATUS status;
PHSVC_API_MSG m;
PVOID apiId = NULL;
memset(&m, 0, sizeof(PHSVC_API_MSG));
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
if (InLength > sizeof(m.p.u.Plugin.i.Data))
return STATUS_BUFFER_OVERFLOW;
m.p.ApiNumber = PhSvcPluginApiNumber;
if (!(apiId = PhSvcpCreateString(ApiId->Buffer, ApiId->Length, &m.p.u.Plugin.i.ApiId)))
return STATUS_NO_MEMORY;
if (InLength != 0)
memcpy(m.p.u.Plugin.i.Data, InBuffer, InLength);
status = PhSvcpCallServer(&m);
if (OutLength != 0)
memcpy(OutBuffer, m.p.u.Plugin.o.Data, min(OutLength, sizeof(m.p.u.Plugin.o.Data)));
if (apiId)
PhSvcpFreeHeap(apiId);
return status;
}
NTSTATUS PhSvcpCallExecuteRunAsCommand(
_In_ PHSVC_API_NUMBER ApiNumber,
_In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters
)
{
NTSTATUS status;
PHSVC_API_MSG m;
PVOID userName = NULL;
PVOID password = NULL;
ULONG passwordLength;
PVOID currentDirectory = NULL;
PVOID commandLine = NULL;
PVOID fileName = NULL;
PVOID desktopName = NULL;
PVOID serviceName = NULL;
memset(&m, 0, sizeof(PHSVC_API_MSG));
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = ApiNumber;
m.p.u.ExecuteRunAsCommand.i.ProcessId = Parameters->ProcessId;
m.p.u.ExecuteRunAsCommand.i.LogonType = Parameters->LogonType;
m.p.u.ExecuteRunAsCommand.i.SessionId = Parameters->SessionId;
m.p.u.ExecuteRunAsCommand.i.UseLinkedToken = Parameters->UseLinkedToken;
status = STATUS_NO_MEMORY;
if (Parameters->UserName && !(userName = PhSvcpCreateString(Parameters->UserName, -1, &m.p.u.ExecuteRunAsCommand.i.UserName)))
goto CleanupExit;
if (Parameters->Password)
{
if (!(password = PhSvcpCreateString(Parameters->Password, -1, &m.p.u.ExecuteRunAsCommand.i.Password)))
goto CleanupExit;
passwordLength = m.p.u.ExecuteRunAsCommand.i.Password.Length;
}
if (Parameters->CurrentDirectory && !(currentDirectory = PhSvcpCreateString(Parameters->CurrentDirectory, -1, &m.p.u.ExecuteRunAsCommand.i.CurrentDirectory)))
goto CleanupExit;
if (Parameters->CommandLine && !(commandLine = PhSvcpCreateString(Parameters->CommandLine, -1, &m.p.u.ExecuteRunAsCommand.i.CommandLine)))
goto CleanupExit;
if (Parameters->FileName && !(fileName = PhSvcpCreateString(Parameters->FileName, -1, &m.p.u.ExecuteRunAsCommand.i.FileName)))
goto CleanupExit;
if (Parameters->DesktopName && !(desktopName = PhSvcpCreateString(Parameters->DesktopName, -1, &m.p.u.ExecuteRunAsCommand.i.DesktopName)))
goto CleanupExit;
if (Parameters->ServiceName && !(serviceName = PhSvcpCreateString(Parameters->ServiceName, -1, &m.p.u.ExecuteRunAsCommand.i.ServiceName)))
goto CleanupExit;
status = PhSvcpCallServer(&m);
CleanupExit:
if (serviceName) PhSvcpFreeHeap(serviceName);
if (desktopName) PhSvcpFreeHeap(desktopName);
if (fileName) PhSvcpFreeHeap(fileName);
if (commandLine) PhSvcpFreeHeap(commandLine);
if (currentDirectory) PhSvcpFreeHeap(currentDirectory);
if (password)
{
RtlSecureZeroMemory(password, passwordLength);
PhSvcpFreeHeap(password);
}
if (userName) PhSvcpFreeHeap(userName);
return status;
}
NTSTATUS PhSvcCallExecuteRunAsCommand(
_In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters
)
{
return PhSvcpCallExecuteRunAsCommand(PhSvcExecuteRunAsCommandApiNumber, Parameters);
}
NTSTATUS PhSvcCallUnloadDriver(
_In_opt_ PVOID BaseAddress,
_In_opt_ PWSTR Name
)
{
NTSTATUS status;
PHSVC_API_MSG m;
PVOID name = NULL;
memset(&m, 0, sizeof(PHSVC_API_MSG));
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcUnloadDriverApiNumber;
m.p.u.UnloadDriver.i.BaseAddress = BaseAddress;
if (Name)
{
name = PhSvcpCreateString(Name, -1, &m.p.u.UnloadDriver.i.Name);
if (!name)
return STATUS_NO_MEMORY;
}
status = PhSvcpCallServer(&m);
if (name)
PhSvcpFreeHeap(name);
return status;
}
NTSTATUS PhSvcCallControlProcess(
_In_ HANDLE ProcessId,
_In_ PHSVC_API_CONTROLPROCESS_COMMAND Command,
_In_ ULONG Argument
)
{
PHSVC_API_MSG m;
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcControlProcessApiNumber;
m.p.u.ControlProcess.i.ProcessId = ProcessId;
m.p.u.ControlProcess.i.Command = Command;
m.p.u.ControlProcess.i.Argument = Argument;
return PhSvcpCallServer(&m);
}
NTSTATUS PhSvcCallControlService(
_In_ PWSTR ServiceName,
_In_ PHSVC_API_CONTROLSERVICE_COMMAND Command
)
{
NTSTATUS status;
PHSVC_API_MSG m;
PVOID serviceName;
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcControlServiceApiNumber;
serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.ControlService.i.ServiceName);
m.p.u.ControlService.i.Command = Command;
if (serviceName)
{
status = PhSvcpCallServer(&m);
}
else
{
status = STATUS_NO_MEMORY;
}
if (serviceName)
PhSvcpFreeHeap(serviceName);
return status;
}
NTSTATUS PhSvcCallCreateService(
_In_ PWSTR ServiceName,
_In_opt_ PWSTR DisplayName,
_In_ ULONG ServiceType,
_In_ ULONG StartType,
_In_ ULONG ErrorControl,
_In_opt_ PWSTR BinaryPathName,
_In_opt_ PWSTR LoadOrderGroup,
_Out_opt_ PULONG TagId,
_In_opt_ PWSTR Dependencies,
_In_opt_ PWSTR ServiceStartName,
_In_opt_ PWSTR Password
)
{
NTSTATUS status;
PHSVC_API_MSG m;
PVOID serviceName = NULL;
PVOID displayName = NULL;
PVOID binaryPathName = NULL;
PVOID loadOrderGroup = NULL;
PVOID dependencies = NULL;
PVOID serviceStartName = NULL;
PVOID password = NULL;
ULONG passwordLength;
memset(&m, 0, sizeof(PHSVC_API_MSG));
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcCreateServiceApiNumber;
m.p.u.CreateService.i.ServiceType = ServiceType;
m.p.u.CreateService.i.StartType = StartType;
m.p.u.CreateService.i.ErrorControl = ErrorControl;
m.p.u.CreateService.i.TagIdSpecified = TagId != NULL;
status = STATUS_NO_MEMORY;
if (!(serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.CreateService.i.ServiceName)))
goto CleanupExit;
if (DisplayName && !(displayName = PhSvcpCreateString(DisplayName, -1, &m.p.u.CreateService.i.DisplayName)))
goto CleanupExit;
if (BinaryPathName && !(binaryPathName = PhSvcpCreateString(BinaryPathName, -1, &m.p.u.CreateService.i.BinaryPathName)))
goto CleanupExit;
if (LoadOrderGroup && !(loadOrderGroup = PhSvcpCreateString(LoadOrderGroup, -1, &m.p.u.CreateService.i.LoadOrderGroup)))
goto CleanupExit;
if (Dependencies)
{
SIZE_T dependenciesLength;
SIZE_T partCount;
PWSTR part;
dependenciesLength = sizeof(WCHAR);
part = Dependencies;
do
{
partCount = PhCountStringZ(part) + 1;
part += partCount;
dependenciesLength += partCount * sizeof(WCHAR);
} while (partCount != 1); // stop at empty dependency part
if (!(dependencies = PhSvcpCreateString(Dependencies, dependenciesLength, &m.p.u.CreateService.i.Dependencies)))
goto CleanupExit;
}
if (ServiceStartName && !(serviceStartName = PhSvcpCreateString(ServiceStartName, -1, &m.p.u.CreateService.i.ServiceStartName)))
goto CleanupExit;
if (Password)
{
if (!(password = PhSvcpCreateString(Password, -1, &m.p.u.CreateService.i.Password)))
goto CleanupExit;
passwordLength = m.p.u.CreateService.i.Password.Length;
}
status = PhSvcpCallServer(&m);
if (NT_SUCCESS(status))
{
if (TagId)
*TagId = m.p.u.CreateService.o.TagId;
}
CleanupExit:
if (password)
{
RtlSecureZeroMemory(password, passwordLength);
PhSvcpFreeHeap(password);
}
if (serviceStartName) PhSvcpFreeHeap(serviceStartName);
if (dependencies) PhSvcpFreeHeap(dependencies);
if (loadOrderGroup) PhSvcpFreeHeap(loadOrderGroup);
if (binaryPathName) PhSvcpFreeHeap(binaryPathName);
if (displayName) PhSvcpFreeHeap(displayName);
if (serviceName) PhSvcpFreeHeap(serviceName);
return status;
}
NTSTATUS PhSvcCallChangeServiceConfig(
_In_ PWSTR ServiceName,
_In_ ULONG ServiceType,
_In_ ULONG StartType,
_In_ ULONG ErrorControl,
_In_opt_ PWSTR BinaryPathName,
_In_opt_ PWSTR LoadOrderGroup,
_Out_opt_ PULONG TagId,
_In_opt_ PWSTR Dependencies,
_In_opt_ PWSTR ServiceStartName,
_In_opt_ PWSTR Password,
_In_opt_ PWSTR DisplayName
)
{
NTSTATUS status;
PHSVC_API_MSG m;
PVOID serviceName = NULL;
PVOID binaryPathName = NULL;
PVOID loadOrderGroup = NULL;
PVOID dependencies = NULL;
PVOID serviceStartName = NULL;
PVOID password = NULL;
ULONG passwordLength;
PVOID displayName = NULL;
memset(&m, 0, sizeof(PHSVC_API_MSG));
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcChangeServiceConfigApiNumber;
m.p.u.ChangeServiceConfig.i.ServiceType = ServiceType;
m.p.u.ChangeServiceConfig.i.StartType = StartType;
m.p.u.ChangeServiceConfig.i.ErrorControl = ErrorControl;
m.p.u.ChangeServiceConfig.i.TagIdSpecified = TagId != NULL;
status = STATUS_NO_MEMORY;
if (!(serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.ChangeServiceConfig.i.ServiceName)))
goto CleanupExit;
if (BinaryPathName && !(binaryPathName = PhSvcpCreateString(BinaryPathName, -1, &m.p.u.ChangeServiceConfig.i.BinaryPathName)))
goto CleanupExit;
if (LoadOrderGroup && !(loadOrderGroup = PhSvcpCreateString(LoadOrderGroup, -1, &m.p.u.ChangeServiceConfig.i.LoadOrderGroup)))
goto CleanupExit;
if (Dependencies)
{
SIZE_T dependenciesLength;
SIZE_T partCount;
PWSTR part;
dependenciesLength = sizeof(WCHAR);
part = Dependencies;
do
{
partCount = PhCountStringZ(part) + 1;
part += partCount;
dependenciesLength += partCount * sizeof(WCHAR);
} while (partCount != 1); // stop at empty dependency part
if (!(dependencies = PhSvcpCreateString(Dependencies, dependenciesLength, &m.p.u.ChangeServiceConfig.i.Dependencies)))
goto CleanupExit;
}
if (ServiceStartName && !(serviceStartName = PhSvcpCreateString(ServiceStartName, -1, &m.p.u.ChangeServiceConfig.i.ServiceStartName)))
goto CleanupExit;
if (Password)
{
if (!(password = PhSvcpCreateString(Password, -1, &m.p.u.ChangeServiceConfig.i.Password)))
goto CleanupExit;
passwordLength = m.p.u.ChangeServiceConfig.i.Password.Length;
}
if (DisplayName && !(displayName = PhSvcpCreateString(DisplayName, -1, &m.p.u.ChangeServiceConfig.i.DisplayName)))
goto CleanupExit;
status = PhSvcpCallServer(&m);
if (NT_SUCCESS(status))
{
if (TagId)
*TagId = m.p.u.ChangeServiceConfig.o.TagId;
}
CleanupExit:
if (displayName) PhSvcpFreeHeap(displayName);
if (password)
{
RtlSecureZeroMemory(password, passwordLength);
PhSvcpFreeHeap(password);
}
if (serviceStartName) PhSvcpFreeHeap(serviceStartName);
if (dependencies) PhSvcpFreeHeap(dependencies);
if (loadOrderGroup) PhSvcpFreeHeap(loadOrderGroup);
if (binaryPathName) PhSvcpFreeHeap(binaryPathName);
if (serviceName) PhSvcpFreeHeap(serviceName);
return status;
}
PVOID PhSvcpPackRoot(
_Inout_ PPH_BYTES_BUILDER BytesBuilder,
_In_ PVOID Buffer,
_In_ SIZE_T Length
)
{
return PhAppendBytesBuilderEx(BytesBuilder, Buffer, Length, sizeof(ULONG_PTR), NULL);
}
VOID PhSvcpPackBuffer_V(
_Inout_ PPH_BYTES_BUILDER BytesBuilder,
_Inout_ PVOID *PointerInBytesBuilder,
_In_ SIZE_T Length,
_In_ SIZE_T Alignment,
_In_ ULONG NumberOfPointersToRebase,
_In_ va_list ArgPtr
)
{
va_list argptr;
ULONG_PTR oldBase;
SIZE_T oldLength;
ULONG_PTR newBase;
SIZE_T offset;
ULONG i;
PVOID *pointer;
oldBase = (ULONG_PTR)BytesBuilder->Bytes->Buffer;
oldLength = BytesBuilder->Bytes->Length;
assert((ULONG_PTR)PointerInBytesBuilder >= oldBase && (ULONG_PTR)PointerInBytesBuilder + sizeof(PVOID) <= oldBase + oldLength);
if (!*PointerInBytesBuilder)
return;
PhAppendBytesBuilderEx(BytesBuilder, *PointerInBytesBuilder, Length, Alignment, &offset);
newBase = (ULONG_PTR)BytesBuilder->Bytes->Buffer;
PointerInBytesBuilder = (PVOID *)((ULONG_PTR)PointerInBytesBuilder - oldBase + newBase);
*PointerInBytesBuilder = (PVOID)offset;
argptr = ArgPtr;
for (i = 0; i < NumberOfPointersToRebase; i++)
{
pointer = va_arg(argptr, PVOID *);
assert(!*pointer || ((ULONG_PTR)*pointer >= oldBase && (ULONG_PTR)*pointer + sizeof(PVOID) <= oldBase + oldLength));
if (*pointer)
*pointer = (PVOID)((ULONG_PTR)*pointer - oldBase + newBase);
}
}
VOID PhSvcpPackBuffer(
_Inout_ PPH_BYTES_BUILDER BytesBuilder,
_Inout_ PVOID *PointerInBytesBuilder,
_In_ SIZE_T Length,
_In_ SIZE_T Alignment,
_In_ ULONG NumberOfPointersToRebase,
...
)
{
va_list argptr;
va_start(argptr, NumberOfPointersToRebase);
PhSvcpPackBuffer_V(BytesBuilder, PointerInBytesBuilder, Length, Alignment, NumberOfPointersToRebase, argptr);
}
SIZE_T PhSvcpBufferLengthStringZ(
_In_opt_ PWSTR String,
_In_ BOOLEAN Multi
)
{
SIZE_T length = 0;
if (String)
{
if (Multi)
{
PWSTR part = String;
SIZE_T partCount;
while (TRUE)
{
partCount = PhCountStringZ(part);
length += (partCount + 1) * sizeof(WCHAR);
if (partCount == 0)
break;
part += partCount + 1;
}
}
else
{
length = (PhCountStringZ(String) + 1) * sizeof(WCHAR);
}
}
return length;
}
NTSTATUS PhSvcCallChangeServiceConfig2(
_In_ PWSTR ServiceName,
_In_ ULONG InfoLevel,
_In_ PVOID Info
)
{
NTSTATUS status;
PHSVC_API_MSG m;
PVOID serviceName = NULL;
PVOID info = NULL;
PH_BYTES_BUILDER bb;
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcChangeServiceConfig2ApiNumber;
m.p.u.ChangeServiceConfig2.i.InfoLevel = InfoLevel;
if (serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.ChangeServiceConfig2.i.ServiceName))
{
switch (InfoLevel)
{
case SERVICE_CONFIG_FAILURE_ACTIONS:
{
LPSERVICE_FAILURE_ACTIONS failureActions = Info;
LPSERVICE_FAILURE_ACTIONS packedFailureActions;
PhInitializeBytesBuilder(&bb, 200);
packedFailureActions = PhSvcpPackRoot(&bb, failureActions, sizeof(SERVICE_FAILURE_ACTIONS));
PhSvcpPackBuffer(&bb, &packedFailureActions->lpRebootMsg, PhSvcpBufferLengthStringZ(failureActions->lpRebootMsg, FALSE), sizeof(WCHAR),
1, &packedFailureActions);
PhSvcpPackBuffer(&bb, &packedFailureActions->lpCommand, PhSvcpBufferLengthStringZ(failureActions->lpCommand, FALSE), sizeof(WCHAR),
1, &packedFailureActions);
if (failureActions->cActions != 0 && failureActions->lpsaActions)
{
PhSvcpPackBuffer(&bb, &packedFailureActions->lpsaActions, failureActions->cActions * sizeof(SC_ACTION), __alignof(SC_ACTION),
1, &packedFailureActions);
}
info = PhSvcpCreateString(bb.Bytes->Buffer, bb.Bytes->Length, &m.p.u.ChangeServiceConfig2.i.Info);
PhDeleteBytesBuilder(&bb);
}
break;
case SERVICE_CONFIG_DELAYED_AUTO_START_INFO:
info = PhSvcpCreateString(Info, sizeof(SERVICE_DELAYED_AUTO_START_INFO), &m.p.u.ChangeServiceConfig2.i.Info);
break;
case SERVICE_CONFIG_FAILURE_ACTIONS_FLAG:
info = PhSvcpCreateString(Info, sizeof(SERVICE_FAILURE_ACTIONS_FLAG), &m.p.u.ChangeServiceConfig2.i.Info);
break;
case SERVICE_CONFIG_SERVICE_SID_INFO:
info = PhSvcpCreateString(Info, sizeof(SERVICE_SID_INFO), &m.p.u.ChangeServiceConfig2.i.Info);
break;
case SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO:
{
LPSERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo = Info;
LPSERVICE_REQUIRED_PRIVILEGES_INFO packedRequiredPrivilegesInfo;
PhInitializeBytesBuilder(&bb, 100);
packedRequiredPrivilegesInfo = PhSvcpPackRoot(&bb, requiredPrivilegesInfo, sizeof(SERVICE_REQUIRED_PRIVILEGES_INFO));
PhSvcpPackBuffer(&bb, &packedRequiredPrivilegesInfo->pmszRequiredPrivileges, PhSvcpBufferLengthStringZ(requiredPrivilegesInfo->pmszRequiredPrivileges, TRUE), sizeof(WCHAR),
1, &packedRequiredPrivilegesInfo);
info = PhSvcpCreateString(bb.Bytes->Buffer, bb.Bytes->Length, &m.p.u.ChangeServiceConfig2.i.Info);
PhDeleteBytesBuilder(&bb);
}
break;
case SERVICE_CONFIG_PRESHUTDOWN_INFO:
info = PhSvcpCreateString(Info, sizeof(SERVICE_PRESHUTDOWN_INFO), &m.p.u.ChangeServiceConfig2.i.Info);
break;
case SERVICE_CONFIG_TRIGGER_INFO:
{
PSERVICE_TRIGGER_INFO triggerInfo = Info;
PSERVICE_TRIGGER_INFO packedTriggerInfo;
ULONG i;
PSERVICE_TRIGGER packedTrigger;
ULONG j;
PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM packedDataItem;
ULONG alignment;
PhInitializeBytesBuilder(&bb, 400);
packedTriggerInfo = PhSvcpPackRoot(&bb, triggerInfo, sizeof(SERVICE_TRIGGER_INFO));
if (triggerInfo->cTriggers != 0 && triggerInfo->pTriggers)
{
PhSvcpPackBuffer(&bb, &packedTriggerInfo->pTriggers, triggerInfo->cTriggers * sizeof(SERVICE_TRIGGER), __alignof(SERVICE_TRIGGER),
1, &packedTriggerInfo);
for (i = 0; i < triggerInfo->cTriggers; i++)
{
packedTrigger = PhOffsetBytesBuilder(&bb, (SIZE_T)packedTriggerInfo->pTriggers + i * sizeof(SERVICE_TRIGGER));
PhSvcpPackBuffer(&bb, &packedTrigger->pTriggerSubtype, sizeof(GUID), __alignof(GUID),
2, &packedTriggerInfo, &packedTrigger);
if (packedTrigger->cDataItems != 0 && packedTrigger->pDataItems)
{
PhSvcpPackBuffer(&bb, &packedTrigger->pDataItems, packedTrigger->cDataItems * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM), __alignof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM),
2, &packedTriggerInfo, &packedTrigger);
for (j = 0; j < packedTrigger->cDataItems; j++)
{
packedDataItem = PhOffsetBytesBuilder(&bb, (SIZE_T)packedTrigger->pDataItems + j * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM));
alignment = 1;
switch (packedDataItem->dwDataType)
{
case SERVICE_TRIGGER_DATA_TYPE_BINARY:
case SERVICE_TRIGGER_DATA_TYPE_LEVEL:
alignment = sizeof(CHAR);
break;
case SERVICE_TRIGGER_DATA_TYPE_STRING:
alignment = sizeof(WCHAR);
break;
case SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY:
case SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL:
alignment = sizeof(ULONG64);
break;
}
PhSvcpPackBuffer(&bb, &packedDataItem->pData, packedDataItem->cbData, alignment,
3, &packedTriggerInfo, &packedTrigger, &packedDataItem);
}
}
}
}
info = PhSvcpCreateString(bb.Bytes->Buffer, bb.Bytes->Length, &m.p.u.ChangeServiceConfig2.i.Info);
PhDeleteBytesBuilder(&bb);
}
break;
case SERVICE_CONFIG_LAUNCH_PROTECTED:
info = PhSvcpCreateString(Info, sizeof(SERVICE_LAUNCH_PROTECTED_INFO), &m.p.u.ChangeServiceConfig2.i.Info);
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
}
if (serviceName && info)
{
status = PhSvcpCallServer(&m);
}
else
{
status = STATUS_NO_MEMORY;
}
if (info)
PhSvcpFreeHeap(info);
if (serviceName)
PhSvcpFreeHeap(serviceName);
return status;
}
NTSTATUS PhSvcCallSetTcpEntry(
_In_ PVOID TcpRow
)
{
PHSVC_API_MSG m;
struct
{
DWORD dwState;
DWORD dwLocalAddr;
DWORD dwLocalPort;
DWORD dwRemoteAddr;
DWORD dwRemotePort;
} *tcpRow = TcpRow;
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcSetTcpEntryApiNumber;
m.p.u.SetTcpEntry.i.State = tcpRow->dwState;
m.p.u.SetTcpEntry.i.LocalAddress = tcpRow->dwLocalAddr;
m.p.u.SetTcpEntry.i.LocalPort = tcpRow->dwLocalPort;
m.p.u.SetTcpEntry.i.RemoteAddress = tcpRow->dwRemoteAddr;
m.p.u.SetTcpEntry.i.RemotePort = tcpRow->dwRemotePort;
return PhSvcpCallServer(&m);
}
NTSTATUS PhSvcCallControlThread(
_In_ HANDLE ThreadId,
_In_ PHSVC_API_CONTROLTHREAD_COMMAND Command,
_In_ ULONG Argument
)
{
PHSVC_API_MSG m;
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcControlThreadApiNumber;
m.p.u.ControlThread.i.ThreadId = ThreadId;
m.p.u.ControlThread.i.Command = Command;
m.p.u.ControlThread.i.Argument = Argument;
return PhSvcpCallServer(&m);
}
NTSTATUS PhSvcCallAddAccountRight(
_In_ PSID AccountSid,
_In_ PUNICODE_STRING UserRight
)
{
NTSTATUS status;
PHSVC_API_MSG m;
PVOID accountSid = NULL;
PVOID userRight = NULL;
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcAddAccountRightApiNumber;
status = STATUS_NO_MEMORY;
if (!(accountSid = PhSvcpCreateString(AccountSid, RtlLengthSid(AccountSid), &m.p.u.AddAccountRight.i.AccountSid)))
goto CleanupExit;
if (!(userRight = PhSvcpCreateString(UserRight->Buffer, UserRight->Length, &m.p.u.AddAccountRight.i.UserRight)))
goto CleanupExit;
status = PhSvcpCallServer(&m);
CleanupExit:
if (userRight) PhSvcpFreeHeap(userRight);
if (accountSid) PhSvcpFreeHeap(accountSid);
return status;
}
NTSTATUS PhSvcCallInvokeRunAsService(
_In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters
)
{
return PhSvcpCallExecuteRunAsCommand(PhSvcInvokeRunAsServiceApiNumber, Parameters);
}
NTSTATUS PhSvcCallIssueMemoryListCommand(
_In_ SYSTEM_MEMORY_LIST_COMMAND Command
)
{
PHSVC_API_MSG m;
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcIssueMemoryListCommandApiNumber;
m.p.u.IssueMemoryListCommand.i.Command = Command;
return PhSvcpCallServer(&m);
}
NTSTATUS PhSvcCallPostMessage(
_In_opt_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PHSVC_API_MSG m;
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcPostMessageApiNumber;
m.p.u.PostMessage.i.hWnd = hWnd;
m.p.u.PostMessage.i.Msg = Msg;
m.p.u.PostMessage.i.wParam = wParam;
m.p.u.PostMessage.i.lParam = lParam;
return PhSvcpCallServer(&m);
}
NTSTATUS PhSvcCallSendMessage(
_In_opt_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PHSVC_API_MSG m;
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcSendMessageApiNumber;
m.p.u.PostMessage.i.hWnd = hWnd;
m.p.u.PostMessage.i.Msg = Msg;
m.p.u.PostMessage.i.wParam = wParam;
m.p.u.PostMessage.i.lParam = lParam;
return PhSvcpCallServer(&m);
}
NTSTATUS PhSvcCallCreateProcessIgnoreIfeoDebugger(
_In_ PWSTR FileName
)
{
NTSTATUS status;
PHSVC_API_MSG m;
PVOID fileName = NULL;
memset(&m, 0, sizeof(PHSVC_API_MSG));
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcCreateProcessIgnoreIfeoDebuggerApiNumber;
fileName = PhSvcpCreateString(FileName, -1, &m.p.u.CreateProcessIgnoreIfeoDebugger.i.FileName);
if (!fileName)
return STATUS_NO_MEMORY;
status = PhSvcpCallServer(&m);
if (fileName)
PhSvcpFreeHeap(fileName);
return status;
}
PSECURITY_DESCRIPTOR PhpAbsoluteToSelfRelativeSD(
_In_ PSECURITY_DESCRIPTOR AbsoluteSecurityDescriptor,
_Out_ PULONG BufferSize
)
{
NTSTATUS status;
ULONG bufferSize = 0;
PSECURITY_DESCRIPTOR selfRelativeSecurityDescriptor;
status = RtlAbsoluteToSelfRelativeSD(AbsoluteSecurityDescriptor, NULL, &bufferSize);
if (status != STATUS_BUFFER_TOO_SMALL)
return NULL;
selfRelativeSecurityDescriptor = PhAllocate(bufferSize);
status = RtlAbsoluteToSelfRelativeSD(AbsoluteSecurityDescriptor, selfRelativeSecurityDescriptor, &bufferSize);
if (!NT_SUCCESS(status))
{
PhFree(selfRelativeSecurityDescriptor);
return NULL;
}
*BufferSize = bufferSize;
return selfRelativeSecurityDescriptor;
}
NTSTATUS PhSvcCallSetServiceSecurity(
_In_ PWSTR ServiceName,
_In_ SECURITY_INFORMATION SecurityInformation,
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor
)
{
NTSTATUS status;
PHSVC_API_MSG m;
PSECURITY_DESCRIPTOR selfRelativeSecurityDescriptor = NULL;
ULONG bufferSize;
PVOID serviceName = NULL;
PVOID copiedSelfRelativeSecurityDescriptor = NULL;
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
selfRelativeSecurityDescriptor = PhpAbsoluteToSelfRelativeSD(SecurityDescriptor, &bufferSize);
if (!selfRelativeSecurityDescriptor)
{
status = STATUS_BAD_DESCRIPTOR_FORMAT;
goto CleanupExit;
}
m.p.ApiNumber = PhSvcSetServiceSecurityApiNumber;
m.p.u.SetServiceSecurity.i.SecurityInformation = SecurityInformation;
status = STATUS_NO_MEMORY;
if (!(serviceName = PhSvcpCreateString(ServiceName, -1, &m.p.u.SetServiceSecurity.i.ServiceName)))
goto CleanupExit;
if (!(copiedSelfRelativeSecurityDescriptor = PhSvcpCreateString(selfRelativeSecurityDescriptor, bufferSize, &m.p.u.SetServiceSecurity.i.SecurityDescriptor)))
goto CleanupExit;
status = PhSvcpCallServer(&m);
CleanupExit:
if (selfRelativeSecurityDescriptor) PhFree(selfRelativeSecurityDescriptor);
if (serviceName) PhSvcpFreeHeap(serviceName);
if (copiedSelfRelativeSecurityDescriptor) PhSvcpFreeHeap(copiedSelfRelativeSecurityDescriptor);
return status;
}
NTSTATUS PhSvcCallLoadDbgHelp(
_In_ PWSTR DbgHelpPath
)
{
NTSTATUS status;
PHSVC_API_MSG m;
PVOID dbgHelpPath = NULL;
memset(&m, 0, sizeof(PHSVC_API_MSG));
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
m.p.ApiNumber = PhSvcLoadDbgHelpApiNumber;
dbgHelpPath = PhSvcpCreateString(DbgHelpPath, -1, &m.p.u.LoadDbgHelp.i.DbgHelpPath);
if (!dbgHelpPath)
return STATUS_NO_MEMORY;
status = PhSvcpCallServer(&m);
if (dbgHelpPath)
PhSvcpFreeHeap(dbgHelpPath);
return status;
}
NTSTATUS PhSvcCallWriteMiniDumpProcess(
_In_ HANDLE ProcessHandle,
_In_ HANDLE ProcessId,
_In_ HANDLE FileHandle,
_In_ ULONG DumpType
)
{
NTSTATUS status;
PHSVC_API_MSG m;
HANDLE serverHandle = NULL;
HANDLE remoteProcessHandle = NULL;
HANDLE remoteFileHandle = NULL;
memset(&m, 0, sizeof(PHSVC_API_MSG));
if (!PhSvcClPortHandle)
return STATUS_PORT_DISCONNECTED;
// For typical uses of this function, the client has more privileges than the server.
// We therefore duplicate our handles into the server's process.
m.p.ApiNumber = PhSvcWriteMiniDumpProcessApiNumber;
if (!NT_SUCCESS(status = PhOpenProcess(&serverHandle, PROCESS_DUP_HANDLE, PhSvcClServerProcessId)))
{
goto CleanupExit;
}
if (!NT_SUCCESS(status = PhDuplicateObject(NtCurrentProcess(), ProcessHandle, serverHandle, &remoteProcessHandle,
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, 0)))
{
goto CleanupExit;
}
if (!NT_SUCCESS(status = PhDuplicateObject(NtCurrentProcess(), FileHandle, serverHandle, &remoteFileHandle,
FILE_GENERIC_WRITE, 0, 0)))
{
goto CleanupExit;
}
m.p.u.WriteMiniDumpProcess.i.LocalProcessHandle = HandleToUlong(remoteProcessHandle);
m.p.u.WriteMiniDumpProcess.i.ProcessId = HandleToUlong(ProcessId);
m.p.u.WriteMiniDumpProcess.i.LocalFileHandle = HandleToUlong(remoteFileHandle);
m.p.u.WriteMiniDumpProcess.i.DumpType = DumpType;
status = PhSvcpCallServer(&m);
CleanupExit:
if (serverHandle)
{
if (remoteProcessHandle)
PhDuplicateObject(serverHandle, remoteProcessHandle, NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
if (remoteFileHandle)
PhDuplicateObject(serverHandle, remoteFileHandle, NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
NtClose(serverHandle);
}
return status;
}