如何防止一个应用程序在任务管理器中被杀死
本文关键字:任务管理器 应用程序 一个 何防止 | 更新日期: 2023-09-27 18:17:27
我正在开发一个家长控制应用程序(用WPF编写),并希望不允许任何人(包括管理员)杀死我的进程。不久前,我在网上找到了下面的代码,它几乎可以完美地工作,除了有时不起作用。
static void SetAcl()
{
var sd = new RawSecurityDescriptor(ControlFlags.None, new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), null, null, new RawAcl(2, 0));
sd.SetFlags(ControlFlags.DiscretionaryAclPresent | ControlFlags.DiscretionaryAclDefaulted);
var rawSd = new byte[sd.BinaryLength];
sd.GetBinaryForm(rawSd, 0);
if (!Win32.SetKernelObjectSecurity(Process.GetCurrentProcess().Handle, SecurityInfos.DiscretionaryAcl, rawSd))
throw new Win32Exception();
}
在Win7中,如果应用程序是由登录用户启动的,即使管理员也无法杀死该进程(拒绝访问)。但是,如果您切换到另一个用户帐户(admin或标准用户),然后勾选"为所有用户显示进程",那么您就可以毫无问题地终止该进程。谁能给我一个提示为什么和如何解决它?
编辑:
我知道有些人对这个问题感到不安,但这是我的困境。这是一个家长控制,我写的主要是为我自己使用。主要功能是我想监控和限制我的孩子玩游戏(而不是简单地关闭所有游戏)。我可以给孩子们分配一个标准的用户账号,他们也不能终止这个进程。然而,有些游戏(如《Mabinogi》)需要管理员权限才能玩。所以,我每次都得输入我的管理员密码,这很烦人。
顺便说一下,我不确定它是否违反Stackoverflow的政策,这是我的应用程序,如果你想看看:https://sites.google.com/site/goppieinc/pc-screen-watcher.
编辑:
我写这篇文章的主要目的是问一下,是否有人能给我一个提示,为什么所发布的代码并不总是有效——例如,如果你为所有用户显示流程。
有些评论是对的,你在玩一个很可能注定没有尽头的游戏。然而,据我所知,将进程设置为关键内核进程似乎会给您带来明显的胜利。任何试图终止该进程的尝试都只会使您的计算机BSOD。代码:
/*
Copyright © 2017 Jesse Nicholson
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace MyRedactedNamespace
{
/// <summary>
/// Class responsible for exposing undocumented functionality making the host process unkillable.
/// </summary>
public static class ProcessProtection
{
[DllImport("ntdll.dll", SetLastError = true)]
private static extern void RtlSetProcessIsCritical(UInt32 v1, UInt32 v2, UInt32 v3);
/// <summary>
/// Flag for maintaining the state of protection.
/// </summary>
private static volatile bool s_isProtected = false;
/// <summary>
/// For synchronizing our current state.
/// </summary>
private static ReaderWriterLockSlim s_isProtectedLock = new ReaderWriterLockSlim();
/// <summary>
/// Gets whether or not the host process is currently protected.
/// </summary>
public static bool IsProtected
{
get
{
try
{
s_isProtectedLock.EnterReadLock();
return s_isProtected;
}
finally
{
s_isProtectedLock.ExitReadLock();
}
}
}
/// <summary>
/// If not alreay protected, will make the host process a system-critical process so it
/// cannot be terminated without causing a shutdown of the entire system.
/// </summary>
public static void Protect()
{
try
{
s_isProtectedLock.EnterWriteLock();
if(!s_isProtected)
{
System.Diagnostics.Process.EnterDebugMode();
RtlSetProcessIsCritical(1, 0, 0);
s_isProtected = true;
}
}
finally
{
s_isProtectedLock.ExitWriteLock();
}
}
/// <summary>
/// If already protected, will remove protection from the host process, so that it will no
/// longer be a system-critical process and thus will be able to shut down safely.
/// </summary>
public static void Unprotect()
{
try
{
s_isProtectedLock.EnterWriteLock();
if(s_isProtected)
{
RtlSetProcessIsCritical(0, 0, 0);
s_isProtected = false;
}
}
finally
{
s_isProtectedLock.ExitWriteLock();
}
}
}
}
这里的想法是,你调用Protect()
方法尽快,然后调用Unprotect()
当你做一个自愿关闭应用程序。
对于WPF应用程序,您将想要钩子SessionEnding
事件,这是您将调用Unprotect()
方法的地方,以防有人注销或关闭计算机。这绝对是SessionEnding
事件,而不是SystemEvents.SessionEnded
事件。通常在调用SystemEvents.SessionEnded
事件时,如果您花费太长时间来释放保护,则应用程序可能会被强制终止,导致每次重新启动或注销时都出现BSOD。如果使用SessionEnding
事件,就可以避免这个问题。关于该事件的另一个有趣的事实是,在某种程度上,您可以对注销或关闭进行抗辩。当然,您还需要在Application.Exit
事件处理程序中调用Unprotect()
。
在部署此机制之前确保您的应用程序是稳定的,因为如果进程受到保护,崩溃也会使您的计算机BSOD。
作为对所有攻击你采取这些措施的人的提醒,请忽略他们。如果有人可能会使用这些代码做一些恶意的事情,这并不重要,这是一个糟糕的借口,可以为了一个完全合法的原因而停止完全合法的研究。在我的例子中,我把它作为我自己的应用程序的一部分来开发,因为成人(管理员)不希望能够通过杀死进程来停止我的进程。他们明确地想要这个功能,因为它阻止他们能够绕过软件的设计目的。
使WPF端只是一个客户端。这里的"服务器"必须是Windows服务。然后将服务设置为自动启动(最后一部分需要管理员权限)。如果它以网络管理员的身份运行,那就有好处了。
如果该服务的进程被杀死,Windows将立即重新启动它。然后,无论用户尝试什么,他们都无法真正停止程序的逻辑,除非他们拥有管理权限并自己停止服务。只使用WPF GUI进行配置。
系统帐户级别高于administrator(至少对于OS)。系统帐号和admin帐号具有相同的文件权限,但功能不同。系统帐户用于操作系统和在Windows下运行的服务。Windows中有许多服务和进程需要能够在内部登录(例如在Windows安装期间)。系统帐户就是为此目的而设计的;它是一个内部帐户,不会出现在用户管理器中,不能添加到任何组,也不能被分配用户权限。
因此,挑战在于如何在安装过程中将您的应用程序特权提升到系统帐户。我不知道如何提升你的过程。但值得一读后参考1,参考2。此外,即使我们假设您成功地将其转移到系统帐户,当涉及到管理您自己的应用程序时,即使您是一个指数级超级用户,您仍然可能面临更多的挑战。
你不能用这段代码阻止管理员杀死你的进程或停止你的服务,但有些时候可能会发生一些事情。
//Obtaining the process DACL
[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);
}
//Updating the process DACL
[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();
}
//Getting the current process
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentProcess();
//Process access rights
[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.
}
public Form1()
{
InitializeComponent();
//Put it all together to prevent users from killing your service or process
IntPtr hProcess = GetCurrentProcess(); // Get the current process handle
// Read the DACL
var dacl = GetProcessSecurityDescriptor(hProcess);
// Insert the new ACE
dacl.DiscretionaryAcl.InsertAce(
0,
new CommonAce(
AceFlags.None,
AceQualifier.AccessDenied,
(int)ProcessAccessRights.PROCESS_ALL_ACCESS,
new SecurityIdentifier(WellKnownSidType.WorldSid, null),
false,
null)
);
// Save the DACL
SetProcessSecurityDescriptor(hProcess, dacl);
}
参考:如何防止用户终止你的服务或进程