mirror of https://github.com/winsw/winsw
Fix RunawayProcessKiller
parent
466b5264e1
commit
cff7360118
|
@ -0,0 +1,180 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace winsw.Plugins.RunawayProcessKiller
|
||||
{
|
||||
public partial class RunawayProcessKillerExtension
|
||||
{
|
||||
internal static class NativeMethods
|
||||
{
|
||||
private const string Kernel32 = "kernel32.dll";
|
||||
private const string NTDll = "ntdll.dll";
|
||||
|
||||
[DllImport(Kernel32)]
|
||||
internal static extern int IsWow64Process(IntPtr hProcess, out int Wow64Process);
|
||||
|
||||
[DllImport(NTDll)]
|
||||
internal static extern int NtQueryInformationProcess(
|
||||
IntPtr ProcessHandle,
|
||||
PROCESSINFOCLASS ProcessInformationClass,
|
||||
out PROCESS_BASIC_INFORMATION32 ProcessInformation,
|
||||
int ProcessInformationLength,
|
||||
IntPtr ReturnLength = default);
|
||||
|
||||
[DllImport(NTDll)]
|
||||
internal static extern int NtQueryInformationProcess(
|
||||
IntPtr ProcessHandle,
|
||||
PROCESSINFOCLASS ProcessInformationClass,
|
||||
out PROCESS_BASIC_INFORMATION64 ProcessInformation,
|
||||
int ProcessInformationLength,
|
||||
IntPtr ReturnLength = default);
|
||||
|
||||
[DllImport(NTDll)]
|
||||
internal static extern unsafe int NtReadVirtualMemory(
|
||||
IntPtr ProcessHandle,
|
||||
IntPtr BaseAddress,
|
||||
void* Buffer,
|
||||
IntPtr BufferSize,
|
||||
IntPtr NumberOfBytesRead = default);
|
||||
|
||||
[DllImport(NTDll)]
|
||||
internal static extern int NtWow64QueryInformationProcess64(
|
||||
IntPtr ProcessHandle,
|
||||
PROCESSINFOCLASS ProcessInformationClass,
|
||||
out PROCESS_BASIC_INFORMATION64 ProcessInformation,
|
||||
int ProcessInformationLength,
|
||||
IntPtr ReturnLength = default);
|
||||
|
||||
[DllImport(NTDll)]
|
||||
internal static extern unsafe int NtWow64ReadVirtualMemory64(
|
||||
IntPtr ProcessHandle,
|
||||
long BaseAddress,
|
||||
void* Buffer,
|
||||
long BufferSize,
|
||||
long NumberOfBytesRead = default);
|
||||
|
||||
internal enum PROCESSINFOCLASS
|
||||
{
|
||||
ProcessBasicInformation = 0,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal readonly struct MEMORY_BASIC_INFORMATION
|
||||
{
|
||||
public readonly IntPtr BaseAddress;
|
||||
private readonly IntPtr AllocationBase;
|
||||
private readonly uint AllocationProtect;
|
||||
public readonly IntPtr RegionSize;
|
||||
private readonly uint State;
|
||||
private readonly uint Protect;
|
||||
private readonly uint Type;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct PROCESS_BASIC_INFORMATION32
|
||||
{
|
||||
private readonly int Reserved1;
|
||||
public readonly int PebBaseAddress;
|
||||
private fixed int Reserved2[2];
|
||||
private readonly uint UniqueProcessId;
|
||||
private readonly int Reserved3;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct PROCESS_BASIC_INFORMATION64
|
||||
{
|
||||
private readonly long Reserved1;
|
||||
public readonly long PebBaseAddress;
|
||||
private fixed long Reserved2[2];
|
||||
private readonly ulong UniqueProcessId;
|
||||
private readonly long Reserved3;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct PEB32
|
||||
{
|
||||
private fixed byte Reserved1[2];
|
||||
private readonly byte BeingDebugged;
|
||||
private fixed byte Reserved2[1];
|
||||
private fixed int Reserved3[2];
|
||||
private readonly int Ldr;
|
||||
public readonly int ProcessParameters;
|
||||
private fixed int Reserved4[3];
|
||||
private readonly int AtlThunkSListPtr;
|
||||
private readonly int Reserved5;
|
||||
private readonly uint Reserved6;
|
||||
private readonly int Reserved7;
|
||||
private readonly uint Reserved8;
|
||||
private readonly uint AtlThunkSListPtr32;
|
||||
private fixed int Reserved9[45];
|
||||
private fixed byte Reserved10[96];
|
||||
private readonly int PostProcessInitRoutine;
|
||||
private fixed byte Reserved11[128];
|
||||
private fixed int Reserved12[1];
|
||||
private readonly uint SessionId;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct PEB64
|
||||
{
|
||||
private fixed byte Reserved1[2];
|
||||
private readonly byte BeingDebugged;
|
||||
private fixed byte Reserved2[1];
|
||||
private fixed long Reserved3[2];
|
||||
private readonly long Ldr;
|
||||
public readonly long ProcessParameters;
|
||||
private fixed long Reserved4[3];
|
||||
private readonly long AtlThunkSListPtr;
|
||||
private readonly long Reserved5;
|
||||
private readonly uint Reserved6;
|
||||
private readonly long Reserved7;
|
||||
private readonly uint Reserved8;
|
||||
private readonly uint AtlThunkSListPtr32;
|
||||
private fixed long Reserved9[45];
|
||||
private fixed byte Reserved10[96];
|
||||
private readonly long PostProcessInitRoutine;
|
||||
private fixed byte Reserved11[128];
|
||||
private fixed long Reserved12[1];
|
||||
private readonly uint SessionId;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct RTL_USER_PROCESS_PARAMETERS32
|
||||
{
|
||||
private fixed byte Reserved1[16];
|
||||
private fixed int Reserved2[10];
|
||||
private readonly UNICODE_STRING32 ImagePathName;
|
||||
private readonly UNICODE_STRING32 CommandLine;
|
||||
|
||||
internal readonly int Environment;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct RTL_USER_PROCESS_PARAMETERS64
|
||||
{
|
||||
private fixed byte Reserved1[16];
|
||||
private fixed long Reserved2[10];
|
||||
private readonly UNICODE_STRING64 ImagePathName;
|
||||
private readonly UNICODE_STRING64 CommandLine;
|
||||
|
||||
internal readonly long Environment;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal readonly struct UNICODE_STRING32
|
||||
{
|
||||
private readonly ushort Length;
|
||||
private readonly ushort MaximumLength;
|
||||
private readonly int Buffer;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal readonly struct UNICODE_STRING64
|
||||
{
|
||||
private readonly ushort Length;
|
||||
private readonly ushort MaximumLength;
|
||||
private readonly long Buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
<TargetFrameworks>net20;net40;net461;netcoreapp3.1</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version><!-- Populated by AppVeyor --></Version>
|
||||
<RootNamespace>winsw.Plugins.RunawayProcessKiller</RootNamespace>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
|
|
|
@ -7,10 +7,11 @@ using System.Xml;
|
|||
using log4net;
|
||||
using winsw.Extensions;
|
||||
using winsw.Util;
|
||||
using static winsw.Plugins.RunawayProcessKiller.RunawayProcessKillerExtension.NativeMethods;
|
||||
|
||||
namespace winsw.Plugins.RunawayProcessKiller
|
||||
{
|
||||
public class RunawayProcessKillerExtension : AbstractWinSWExtension
|
||||
public partial class RunawayProcessKillerExtension : AbstractWinSWExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Absolute path to the PID file, which stores ID of the previously launched process.
|
||||
|
@ -57,6 +58,127 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
this.CheckWinSWEnvironmentVariable = checkWinSWEnvironmentVariable;
|
||||
}
|
||||
|
||||
private static unsafe string? ReadEnvironmentVariable(IntPtr processHandle, string variable)
|
||||
{
|
||||
if (Environment.Is64BitOperatingSystem)
|
||||
{
|
||||
if (Environment.Is64BitProcess)
|
||||
{
|
||||
return SearchEnvironmentVariable(
|
||||
processHandle,
|
||||
variable,
|
||||
GetEnvironmentAddress64(processHandle).ToInt64(),
|
||||
(handle, address, buffer, size) => NtReadVirtualMemory(handle, new IntPtr(address), buffer, new IntPtr(size)));
|
||||
}
|
||||
|
||||
if (IsWow64Process(processHandle, out int isWow64) == 0 || isWow64 == 0)
|
||||
{
|
||||
return SearchEnvironmentVariable(
|
||||
processHandle,
|
||||
variable,
|
||||
GetEnvironmentAddressWow64(processHandle),
|
||||
(handle, address, buffer, size) => NtWow64ReadVirtualMemory64(handle, address, buffer, size));
|
||||
}
|
||||
}
|
||||
|
||||
return SearchEnvironmentVariable(
|
||||
processHandle,
|
||||
variable,
|
||||
GetEnvironmentAddress32(processHandle).ToInt64(),
|
||||
(handle, address, buffer, size) => NtReadVirtualMemory(handle, new IntPtr(address), buffer, new IntPtr(size)));
|
||||
}
|
||||
|
||||
private unsafe delegate int ReadMemoryCallback(IntPtr processHandle, long baseAddress, void* buffer, int bufferSize);
|
||||
|
||||
private static unsafe string? SearchEnvironmentVariable(IntPtr processHandle, string variable, long address, ReadMemoryCallback reader)
|
||||
{
|
||||
const int BaseBufferSize = 0x1000;
|
||||
string variableKey = '\0' + variable + '=';
|
||||
string buffer = new string('\0', BaseBufferSize + variableKey.Length);
|
||||
fixed (char* bufferPtr = buffer)
|
||||
{
|
||||
long startAddress = address;
|
||||
for (; ; )
|
||||
{
|
||||
int status = reader(processHandle, address, bufferPtr, buffer.Length * sizeof(char));
|
||||
int index = buffer.IndexOf("\0\0");
|
||||
if (index >= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
address += BaseBufferSize * sizeof(char);
|
||||
}
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
int variableIndex = buffer.IndexOf(variableKey);
|
||||
if (variableIndex >= 0)
|
||||
{
|
||||
int valueStartIndex = variableIndex + variableKey.Length;
|
||||
int valueEndIndex = buffer.IndexOf('\0', valueStartIndex);
|
||||
string value = buffer.Substring(valueStartIndex, valueEndIndex - valueStartIndex);
|
||||
return value;
|
||||
}
|
||||
|
||||
address -= BaseBufferSize * sizeof(char);
|
||||
if (address < startAddress)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int status = reader(processHandle, address, bufferPtr, buffer.Length * sizeof(char));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static unsafe IntPtr GetEnvironmentAddress32(IntPtr processHandle)
|
||||
{
|
||||
_ = NtQueryInformationProcess(
|
||||
processHandle,
|
||||
PROCESSINFOCLASS.ProcessBasicInformation,
|
||||
out PROCESS_BASIC_INFORMATION32 information,
|
||||
sizeof(PROCESS_BASIC_INFORMATION32));
|
||||
|
||||
PEB32 peb;
|
||||
_ = NtReadVirtualMemory(processHandle, new IntPtr(information.PebBaseAddress), &peb, new IntPtr(sizeof(PEB32)));
|
||||
RTL_USER_PROCESS_PARAMETERS32 parameters;
|
||||
_ = NtReadVirtualMemory(processHandle, new IntPtr(peb.ProcessParameters), ¶meters, new IntPtr(sizeof(RTL_USER_PROCESS_PARAMETERS32)));
|
||||
return new IntPtr(parameters.Environment);
|
||||
}
|
||||
|
||||
private static unsafe IntPtr GetEnvironmentAddress64(IntPtr processHandle)
|
||||
{
|
||||
_ = NtQueryInformationProcess(
|
||||
processHandle,
|
||||
PROCESSINFOCLASS.ProcessBasicInformation,
|
||||
out PROCESS_BASIC_INFORMATION64 information,
|
||||
sizeof(PROCESS_BASIC_INFORMATION64));
|
||||
|
||||
PEB64 peb;
|
||||
_ = NtReadVirtualMemory(processHandle, new IntPtr(information.PebBaseAddress), &peb, new IntPtr(sizeof(PEB64)));
|
||||
RTL_USER_PROCESS_PARAMETERS64 parameters;
|
||||
_ = NtReadVirtualMemory(processHandle, new IntPtr(peb.ProcessParameters), ¶meters, new IntPtr(sizeof(RTL_USER_PROCESS_PARAMETERS64)));
|
||||
return new IntPtr(parameters.Environment);
|
||||
}
|
||||
|
||||
private static unsafe long GetEnvironmentAddressWow64(IntPtr processHandle)
|
||||
{
|
||||
_ = NtWow64QueryInformationProcess64(
|
||||
processHandle,
|
||||
PROCESSINFOCLASS.ProcessBasicInformation,
|
||||
out PROCESS_BASIC_INFORMATION64 information,
|
||||
sizeof(PROCESS_BASIC_INFORMATION64));
|
||||
|
||||
PEB64 peb;
|
||||
_ = NtWow64ReadVirtualMemory64(processHandle, information.PebBaseAddress, &peb, sizeof(PEB64));
|
||||
RTL_USER_PROCESS_PARAMETERS64 parameters;
|
||||
_ = NtWow64ReadVirtualMemory64(processHandle, peb.ProcessParameters, ¶meters, sizeof(RTL_USER_PROCESS_PARAMETERS64));
|
||||
return parameters.Environment;
|
||||
}
|
||||
|
||||
public override void Configure(ServiceDescriptor descriptor, XmlNode node)
|
||||
{
|
||||
// We expect the upper logic to process any errors
|
||||
|
@ -121,35 +243,15 @@ namespace winsw.Plugins.RunawayProcessKiller
|
|||
}
|
||||
|
||||
// Ensure the process references the service
|
||||
string? affiliatedServiceId;
|
||||
// TODO: This method is not ideal since it works only for vars explicitly mentioned in the start info
|
||||
// No Windows 10- compatible solution for EnvVars retrieval, see https://blog.gapotchenko.com/eazfuscator.net/reading-environment-variables
|
||||
StringDictionary previousProcessEnvVars = proc.StartInfo.EnvironmentVariables;
|
||||
string expectedEnvVarName = WinSWSystem.ENVVAR_NAME_SERVICE_ID;
|
||||
if (previousProcessEnvVars.ContainsKey(expectedEnvVarName))
|
||||
{
|
||||
// StringDictionary is case-insensitive, hence it will fetch variable definitions in any case
|
||||
affiliatedServiceId = previousProcessEnvVars[expectedEnvVarName];
|
||||
}
|
||||
else if (CheckWinSWEnvironmentVariable)
|
||||
string? affiliatedServiceId = ReadEnvironmentVariable(proc.Handle, expectedEnvVarName);
|
||||
if (affiliatedServiceId is null && CheckWinSWEnvironmentVariable)
|
||||
{
|
||||
Logger.Warn("The process " + pid + " has no " + expectedEnvVarName + " environment variable defined. "
|
||||
+ "The process has not been started by WinSW, hence it won't be terminated.");
|
||||
if (Logger.IsDebugEnabled)
|
||||
{
|
||||
// TODO replace by String.Join() in .NET 4
|
||||
string[] keys = new string[previousProcessEnvVars.Count];
|
||||
previousProcessEnvVars.Keys.CopyTo(keys, 0);
|
||||
Logger.DebugFormat("Env vars of the process with PID={0}: {1}", new object[] { pid, string.Join(",", keys) });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We just skip this check
|
||||
affiliatedServiceId = null;
|
||||
}
|
||||
|
||||
// Check the service ID value
|
||||
if (CheckWinSWEnvironmentVariable && !ServiceId.Equals(affiliatedServiceId))
|
||||
|
|
|
@ -65,7 +65,6 @@ namespace winswTests.Extensions
|
|||
}
|
||||
|
||||
[Test]
|
||||
[Ignore(nameof(RunawayProcessKillerExtension) + "isn't working.")]
|
||||
public void ShouldKillTheSpawnedProcess()
|
||||
{
|
||||
var winswId = "myAppWithRunaway";
|
||||
|
|
Loading…
Reference in New Issue