Skip to content

Instantly share code, notes, and snippets.

@nicholasmckinney
Created May 8, 2017 00:32
Show Gist options
  • Save nicholasmckinney/0872a1cac2bc57a5ef8f0e7bed271d77 to your computer and use it in GitHub Desktop.
Save nicholasmckinney/0872a1cac2bc57a5ef8f0e7bed271d77 to your computer and use it in GitHub Desktop.
Process Armor - Prevent users from killing your service or process
using System;
using System.Diagnostics;
using System.Reflection;
using System.ComponentModel;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Configuration.Install;
/*
Step One:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /out:procarmor.exe ProcessArmor.cs
Step Two:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=false /U procarmor.exe
Reference: http://csharptest.net/1043/how-to-prevent-users-from-killing-your-service-process/
Note: An administrator can still kill the process. Just buys you time really. :-)
Chink in ProcArmor:
procarmor.exe chink
procarmor.exe choke
*/
namespace ProcessArmor
{
[System.ComponentModel.RunInstaller(true)]
public class Sample : System.Configuration.Install.Installer
{
//The Methods can be Uninstall/Install. Install is transactional, and really unnecessary.
public override void Uninstall(System.Collections.IDictionary savedState)
{
Console.WriteLine("Hello There From Uninstall");
Program.Main(null);
}
}
class Program
{
public static void Main(string[] args)
{
if (args != null && args.Length > 0 && args[0].Equals("chink", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("Find Chink in Armor...");
Chink();
return;
}
if (args != null && args.Length > 0 && args[0].Equals("choke", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("Find Choke in Armor...");
Choke();
return;
}
Console.WriteLine("Hello World!");
IntPtr hProcess = GetCurrentProcess();
// Read the DACL
var dacl = GetProcessSecurityDescriptor(hProcess);
// Remove ACE
ThreadACE();
for (int i = 0; i < dacl.DiscretionaryAcl.Count; i++)
{
dacl.DiscretionaryAcl.RemoveAce(i);
}
SetProcessSecurityDescriptor(hProcess, dacl);
Console.WriteLine("ACE Removed");
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
public static void Choke()
{
Process[] processlist = Process.GetProcesses();
int nProcessID = Process.GetCurrentProcess().Id;
foreach (Process theprocess in processlist)
{
if (theprocess.ProcessName.Equals(Process.GetCurrentProcess().ProcessName) && theprocess.Id != nProcessID)
{
Console.WriteLine("Process: {0} ID: {1}", theprocess.ProcessName, theprocess.Id);
IntPtr procPtr = OpenProcess(ProcessAccessRights.WRITE_DAC, false, theprocess.Id);
Console.WriteLine("--->Opening {0} for WRITE_DAC {1}", theprocess.Id, procPtr);
//get current process DACL
IntPtr hProcess = GetCurrentProcess();
var dacl = GetProcessSecurityDescriptor(hProcess);
SetProcessSecurityDescriptor(procPtr, dacl);
Console.WriteLine("--->Resetting DACL", theprocess.Id, procPtr);
CloseHandle(procPtr);
procPtr = OpenProcess(ProcessAccessRights.PROCESS_TERMINATE, false, theprocess.Id);
Console.WriteLine("--->Terminating process...", theprocess.Id, procPtr);
TerminateProcess(procPtr, 1);
}
}
}
public static void Chink()
{
Process[] processlist = Process.GetProcesses();
int nProcessID = Process.GetCurrentProcess().Id;
foreach (Process theprocess in processlist)
{
if (theprocess.ProcessName.Equals(Process.GetCurrentProcess().ProcessName) && theprocess.Id != nProcessID)
{
Console.WriteLine("Process: {0} ID: {1}", theprocess.ProcessName, theprocess.Id);
foreach (ProcessThread td in theprocess.Threads)
{
Console.WriteLine("+---Thread ID: {0}", td.Id);
if (theprocess.ProcessName.Equals(Process.GetCurrentProcess().ProcessName) && theprocess.Id != nProcessID)
{
IntPtr tdPtr = OpenThread(ThreadAccess.TERMINATE, false, (uint)td.Id);
Console.WriteLine("--->Exploiting Chink in ProcArmor on thread {0} - {1}", td.Id, TerminateThread(tdPtr));
}
}
}
}
}
public static void ThreadACE()
{
Process[] processlist = Process.GetProcesses();
int nProcessID = Process.GetCurrentProcess().Id;
foreach (Process theprocess in processlist)
{
if (theprocess.ProcessName.Equals(Process.GetCurrentProcess().ProcessName) && theprocess.Id == nProcessID)
{
Console.WriteLine("Process: {0} ID: {1}", theprocess.ProcessName, theprocess.Id);
foreach (ProcessThread td in theprocess.Threads)
{
Console.WriteLine("+---Thread ID: {0}", td.Id);
if (theprocess.ProcessName.Equals(Process.GetCurrentProcess().ProcessName) && theprocess.Id == nProcessID)
{
IntPtr tdPtr = OpenThread(ThreadAccess.THREAD_ALL_ACCESS, false, (uint)td.Id);
var tdacl = GetProcessSecurityDescriptor(tdPtr);
for (int i = 0; i < tdacl.DiscretionaryAcl.Count; i++)
{
tdacl.DiscretionaryAcl.RemoveAce(i);
}
Console.WriteLine("+---Thread ACE Removed: {0}", td.Id);
SetProcessSecurityDescriptor(tdPtr, tdacl);
}
}
}
}
}
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool GetKernelObjectSecurity(IntPtr Handle, int securityInformation, [Out] byte[] pSecurityDescriptor,
uint nLength, out uint lpnLengthNeeded);
public static RawSecurityDescriptor GetProcessSecurityDescriptor(IntPtr processHandle)
{
const int DACL_SECURITY_INFORMATION = 0x00000004;
byte[] psd = new byte[0];
uint bufSizeNeeded;
// Call with 0 size to obtain the actual size needed in bufSizeNeeded
GetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION, psd, 0, out bufSizeNeeded);
if (bufSizeNeeded < 0 || bufSizeNeeded > short.MaxValue)
throw new Win32Exception();
// Allocate the required bytes and obtain the DACL
if (!GetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION,
psd = new byte[bufSizeNeeded], bufSizeNeeded, out bufSizeNeeded))
throw new Win32Exception();
// Use the RawSecurityDescriptor class from System.Security.AccessControl to parse the bytes:
return new RawSecurityDescriptor(psd, 0);
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool SetKernelObjectSecurity(IntPtr Handle, int securityInformation, [In] byte[] pSecurityDescriptor);
public static void SetProcessSecurityDescriptor(IntPtr processHandle, RawSecurityDescriptor dacl)
{
const int DACL_SECURITY_INFORMATION = 0x00000004;
byte[] rawsd = new byte[dacl.BinaryLength];
dacl.GetBinaryForm(rawsd, 0);
if (!SetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION, rawsd))
throw new Win32Exception();
}
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern int TerminateThread(IntPtr hThread);
[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[Flags]
public enum ThreadAccess : int
{
TERMINATE = (0x0001),
SUSPEND_RESUME = (0x0002),
GET_CONTEXT = (0x0008),
SET_CONTEXT = (0x0010),
SET_INFORMATION = (0x0020),
QUERY_INFORMATION = (0x0040),
SET_THREAD_TOKEN = (0x0080),
IMPERSONATE = (0x0100),
DIRECT_IMPERSONATION = (0x0200),
SYNCHRONIZE = (0x00100000),
STANDARD_RIGHTS_REQUIRED = 0x000f0000,
THREAD_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3FF)
}
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(
ProcessAccessRights processAccess,
bool bInheritHandle,
int processId
);
public static IntPtr OpenProcess(Process proc, ProcessAccessRights flags)
{
return OpenProcess(flags, false, proc.Id);
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hHandle);
[Flags]
public enum ProcessAccessRights
{
PROCESS_CREATE_PROCESS = 0x0080, // Required to create a process.
PROCESS_CREATE_THREAD = 0x0002, // Required to create a thread.
PROCESS_DUP_HANDLE = 0x0040, // Required to duplicate a handle using DuplicateHandle.
PROCESS_QUERY_INFORMATION = 0x0400, // Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken, GetExitCodeProcess, GetPriorityClass, and IsProcessInJob).
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, // Required to retrieve certain information about a process (see QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION. Windows Server 2003 and Windows XP/2000: This access right is not supported.
PROCESS_SET_INFORMATION = 0x0200, // Required to set certain information about a process, such as its priority class (see SetPriorityClass).
PROCESS_SET_QUOTA = 0x0100, // Required to set memory limits using SetProcessWorkingSetSize.
PROCESS_SUSPEND_RESUME = 0x0800, // Required to suspend or resume a process.
PROCESS_TERMINATE = 0x0001, // Required to terminate a process using TerminateProcess.
PROCESS_VM_OPERATION = 0x0008, // Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
PROCESS_VM_READ = 0x0010, // Required to read memory in a process using ReadProcessMemory.
PROCESS_VM_WRITE = 0x0020, // Required to write to memory in a process using WriteProcessMemory.
DELETE = 0x00010000, // Required to delete the object.
READ_CONTROL = 0x00020000, // Required to read information in the security descriptor for the object, not including the information in the SACL. To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right. For more information, see SACL Access Right.
SYNCHRONIZE = 0x00100000, // The right to use the object for synchronization. This enables a thread to wait until the object is in the signaled state.
WRITE_DAC = 0x00040000, // Required to modify the DACL in the security descriptor for the object.
WRITE_OWNER = 0x00080000, // Required to change the owner in the security descriptor for the object.
STANDARD_RIGHTS_REQUIRED = 0x000f0000,
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF),// All possible access rights for a process object.
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment